当我学习foreach
时,我在某处读到了这个:
foreach (var element in enumerable)
{
// do something with element
}
基本上等同于:
using (var enumerator = enumerable.GetEnumerator())
{
while (enumerator.MoveNext())
{
var element = enumerator.Current;
// do something with element
}
}
如果IEnumerator
和IEnumerator<T>
都没有实现IDisposable
,为什么此代码甚至会编译? C#语言规范似乎只提到using
上下文中的IDisposable
语句。
这样的using
声明是做什么的?
答案 0 :(得分:4)
请检查以下有关 foreach 声明的link 。如果可能,它使用带有Dispose调用的try / finally块。这是使用语句后面的代码。
答案 1 :(得分:2)
IEnumerator
可能无法实现IDisposable
,但GetEnumerator()
会返回IEnumerator<T>
。来自IEnumerator<T>
上的文档:
此外,IEnumerator实现了IDisposable,它要求您实现Dispose方法。这使您可以在使用其他资源时关闭数据库连接或释放文件句柄或类似操作。如果没有其他资源可以处理,请提供一个空的Dispose实现。
这当然是假设您的枚举是IEnumerable<T>
而不仅仅是IEnumerable
。如果您的原始枚举只是一个IEnumerable
,那么就无法编译。
答案 2 :(得分:1)
似乎没有人回答主要问题:
这种using语句有什么作用?
我将举一个我喜欢的书中的一个例子-C# in Depth的Jon Skeet。这样的函数可能会产生IEnumerator<T>
的结果:
static IEnumerable<string> Iterator()
{
try
{
Console.WriteLine("Before first yield");
yield return "first";
Console.WriteLine("Between yields");
yield return "second";
Console.WriteLine("After second yield");
}
finally
{
Console.WriteLine("In finally block");
}
}
然后您将使用它:
foreach (string value in Iterator())
{
Console.WriteLine("Received value: {0}", value);
if (value != null)
{
break;
}
}
当我们在第一次迭代中中断时,迭代器仅返回序列中的第一个"first"
值。现在,您的迭代器可能如何“知道”在循环结束之前您将不会继续进行,因此它将触发finally
块?这是隐藏的using
语句。如果您无法使用foreach
循环,则以上示例中的代码将如下所示:
IEnumerable<string> enumerable = Iterator();
using (IEnumerator<string> enumerator = enumerable.GetEnumerator())
{
while (enumerator.MoveNext())
{
string value = enumerator.Current;
Console.WriteLine("Received value: {0}", value);
if (value != null)
{
break;
}
}
}
当我们离开隐藏的using
语句的范围时,Dispose()
方法将触发:
finally
{
Console.WriteLine("In finally block");
}
* Jon Skeet提供了所有代码行和上面的某些文本。我本来可以重写它,但是决定宁可保持原样。如果作者不希望我分享它-我将删除答案或尽快将其重写。