foreach自动调用Dispose吗?

62
在C#中,foreach循环会自动调用实现IDisposable接口的任何对象的Dispose方法吗? http://msdn.microsoft.com/en-us/library/aa664754(v=vs.71).aspx似乎表明它确实会这样做:
否则,集合表达式是实现System.IEnumerable类型的,foreach语句的扩展为: Copy
IEnumerator enumerator = 
        ((System.Collections.IEnumerable)(collection)).GetEnumerator();
try {
   while (enumerator.MoveNext()) {
      ElementType element = (ElementType)enumerator.Current;
      statement;
   }
}
finally {
   IDisposable disposable = enumerator as System.IDisposable;
   if (disposable != null) disposable.Dispose();
}
2个回答

53

是的,如果枚举器实现了IDisposable接口,foreach会调用枚举器的Dispose()方法。


8
如果有些困惑的话,它似乎是在对枚举器本身调用Dispose方法,而不是对枚举中的项调用。如果对每个项都调用Dispose方法,那可能会破坏很多场景,并且很难保证异常安全性 :) - Merlyn Morgan-Graham
5
如果 GetEnumerator 方法返回的类型是接口,foreach 方法只会在运行时检查返回对象是否实现了 IDisposable,以决定是否调用 Dispose 方法。否则,编译器将在编译时决定是否调用 Dispose 方法。特别地,如果返回类型是一个可继承的类且未实现 IDisposable 接口,则即使该对象实现了 IDisposable 接口也不会被处理。 - supercat
1
@supercat,请您进一步解释一下,以便我能更好地理解(也许其他人也会受益;-)。从文档(http://msdn.microsoft.com/en-us/library/aa664754%28v=vs.71%29.aspx)中我所了解到的是,当 E enumerator 是一个 final class 并且没有实现 IDisposable 时,实现可以被优化,"在这种情况下,实现可以安全地优化整个 finally 子句。"(=不调用.Dispose())。但是我看不到任何可能出现 E enumerator 确实实现了 IDisposable,但 .Dispose() 没有被调用的情况。 - BatteryBackupUnit
1
@BatteryBackupUnit:我觉得我上面说错了。我主要使用VB.NET,它在许多方面类似于C#,其For Each的行为与描述的相同。 - supercat

18

这个问题/答案的措辞不太清楚。

它没有明确说明所问的问题是:

Q:“foreach在移动到下一个对象之前是否对枚举器返回的对象进行处理?”

A:当然不是。 它仅仅是提供了一种方便的方式,用于在枚举中为每个对象运行一次某些代码。

还是它的意思是:

Q:“在幕后,foreach使用可枚举对象。 即使迭代器块中出现异常,这样做是否会在调用foreach后被处理?”

A:仍然相当显然的是,答案是肯定的! 由于语法不提供您访问枚举器的权限,因此它有责任处置它。

第二个问题的答案就是引起混淆的地方,因为人们听说foreach扩展为具有try/finally块的while块。那个finally的目的是确保如果它实现IDisposable,则枚举器被处理。

如果您需要自己查看:在此处查看示例

希望这有助于澄清! ;)


2
你所说的“foreach使用可枚举对象”实际上是指“foreach使用可枚举集合提供的枚举器”。 - efdee
我已经吃过亏了,我仍然必须处理集合(可枚举):( - snipsnipsnip

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接