当我学习
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
声明有什么作用?
IEnumerator
可能不会实现 IDisposable
但 GetEnumerator()
返回一个 IEnumerator<T>
,它可以实现。来自 IEnumerator<T>
上的文档:
此外,IEnumerator 实现了 IDisposable,这需要您实现 Dispose 方法。这使您可以在使用其他资源时关闭数据库连接或释放文件句柄或类似操作。如果没有其他资源需要处置,请提供一个空的 Dispose 实现。
这当然是假设您的枚举是一个
IEnumerable<T>
而不仅仅是一个 IEnumerable
。如果您的原始枚举只是一个 IEnumerable
那么它将无法编译。
似乎还没有人回答主要问题:
这样的using语句有什么作用?
我将使用我最喜欢的一本书籍中的示例 - Jon Skeet 的《C# in Depth》。您可能会得到由如下函数产生的结果 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 提供。我本来可以重写它,但我决定还是原封不动地保留它。如果作者不想让我分享 - 我会尽快删除答案或重写它。