最近有人指出,各种 Linq 扩展方法(例如 Where
,Select
等)返回的 IEnumerable<T>
也同时是 IDisposable
。以下语句的结果为 True
new int[2] {0,1}.Select(x => x*2) is IDisposable
我需要处理一个Where
表达式的结果吗?
每当我调用返回IEnumerable<T>
的方法时,我(可能)需要在使用完成后调用dispose吗?
最近有人指出,各种 Linq 扩展方法(例如 Where
,Select
等)返回的 IEnumerable<T>
也同时是 IDisposable
。以下语句的结果为 True
new int[2] {0,1}.Select(x => x*2) is IDisposable
我需要处理一个Where
表达式的结果吗?
每当我调用返回IEnumerable<T>
的方法时,我(可能)需要在使用完成后调用dispose吗?
不用担心。
它们返回IDisposable
实现的事实只是一种实现细节 - 这是因为在C#编译器的Microsoft实现中,迭代器块恰好创建了一个同时实现IEnumerable<T>
和IEnumerator<T>
的单个类型。后者扩展了IDisposable
,这就是为什么你看到它的原因。
以下是演示此问题的示例代码:
using System;
using System.Collections.Generic;
public class Test
{
static void Main()
{
IEnumerable<int> foo = Foo();
Console.WriteLine(foo is IDisposable); // Prints True
}
static IEnumerable<int> Foo()
{
yield break;
}
}
请注意,确实需要留意 IEnumerator<T>
实现了 IDisposable
接口。所以,每次显式迭代时,都应该适当地进行释放。例如,如果您想迭代某些内容并确保始终有一个值,可以使用以下代码:
using (var enumerator = enumerable.GetEnumerator())
{
if (!enumerator.MoveNext())
{
throw // some kind of exception;
}
var value = enumerator.Current;
while (enumerator.MoveNext())
{
// Do something with value and enumerator.Current
}
}
(当然,foreach
循环将自动完成这个操作。)
GetEnumerator()
方法的迭代器调用Dispose
会使得第一次调用返回的枚举器失效,但不会影响任何后续调用所返回的枚举器;在第一次GetEnumerator()
调用之前调用Dispose
没有任何效果。 - supercatsupercat
描述的奇怪行为有任何见解? - ean5533GetEnumerator()
返回的是同一个引用;之后,它会创建分开的副本。这样做的想法是在只迭代一次的常见情况下只创建一个对象。 - Jon Skeet