【问题标题】:When using yield within a "using" statement, when does Dispose occur?在“using”语句中使用 yield 时,Dispose 何时发生?
【发布时间】:2013-01-07 21:00:47
【问题描述】:

我有一个关于延迟执行和数据处置的问题。

考虑以下示例:

private IEnumerable<string> ParseFile(string fileName)
{
    using(StreamReader sr = new StreamReader(fileName))
    {
        string line;
        while((line = sr.ReadLine()) != null)
        {
            yield return line;
        }
    }
}

private void LineReader(string fileName)
{
    int counter = 0;

    foreach(string line in ParseFile(fileName))
    {
        if(counter == 2)
        {
            break; // will this cause a dispose on the StreamReader?
        } else
        {
            Console.WriteLine(line);
            counter++;
        }
    }
}

break 语句会立即导致ParseFile 中的读取器释放,还是仍然在上下文中考虑并锁定文件打开直到程序本身关闭?

【问题讨论】:

  • 编写一个快速控制台应用程序并找出答案:)
  • 附注一下,当计数器达到两个时不要使用break,只需在ParseFile 的末尾添加Take(2)
  • 如果您想很好地解释迭代器块中发生的事情,或者像他经常建议的那样,只需查看语言规范,请阅读 Jon Skeet 的“C# in Depth”一书。
  • @Servy 您下面的答案正是我想要的。我知道有很多更好的方法可以从文件中只提取 2 行。这只是一个简单的例子来展示我想要达到的目的。再次感谢您的帮助!

标签: c# file-io using deferred-execution


【解决方案1】:

所以我们这里有几个不同的问题。

首先,处理迭代器块中的usingIEnumerator 扩展 IDisposable。生成迭代器块的代码实际上足够健壮,任何 try/finally 块(using 导致创建 try/finally 块)导致在 Dispose 方法中调用 finally 块的内容枚举器,如果它还没有被调用。所以只要枚举器被释放,就不会泄露StreamReader

所以现在我们问自己,枚举数是否被释放。所有foreach 语句都会在枚举器上调用Dispose(如果它实现IDisposable)。即使您使用 breakreturn 语句退出,它们也会这样做,并且在它正常结束时也是如此。

因此,您可以确保在任何情况下资源都不会泄露,除非没有可以防止泄露(即有人拔掉机器)。

【讨论】:

  • 比这复杂一点。在 OP 的情况下,迭代器绝对会因为 break 语句而被丢弃。但是对 ParseFile 的调用会在“本地使用”内产生对 StreamReader 的引用,该引用现已超出范围。我认为这会将其排入终结器队列并在收集时处理?
  • @n8wrl 不,这不是真的。当IEnumeratorforeach 循环处理时,它将调用StreamReader 上的处理(假设它当时在using 内部)。这是迭代器块的实现方式;使这成为可能。
  • 我想我们应该听从 PsychoDad 的建议并尝试一下,因为如果它是这样工作的,那就太酷了!
  • 试图找到具体的报价,但你可以开始阅读,以防你更快到达那里。 blogs.msdn.com/b/ericlippert/archive/2009/07/16/… 很确定这将是七部分系列中最相关的,或者是之后的一个。
猜你喜欢
  • 2010-10-05
  • 1970-01-01
  • 1970-01-01
  • 2013-10-14
  • 1970-01-01
  • 2021-12-29
  • 1970-01-01
  • 2020-04-04
  • 2011-03-15
相关资源
最近更新 更多