我需要考虑处理我使用的任何IEnumerable<T>吗?

80

最近有人指出,各种 Linq 扩展方法(例如 WhereSelect 等)返回的 IEnumerable<T> 也同时是 IDisposable。以下语句的结果为 True

new int[2] {0,1}.Select(x => x*2) is IDisposable

我需要处理一个Where表达式的结果吗?

每当我调用返回IEnumerable<T>的方法时,我(可能)需要在使用完成后调用dispose吗?

1个回答

94

不用担心。

它们返回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循环将自动完成这个操作。)


2
那么只要我不显式地使用枚举器,我就不需要进行处理吗? - Rob
2
有趣的是,对于已经调用过 GetEnumerator() 方法的迭代器调用 Dispose 会使得第一次调用返回的枚举器失效,但不会影响任何后续调用所返回的枚举器;在第一次 GetEnumerator() 调用之前调用 Dispose 没有任何效果。 - supercat
1
@JonSkeet,您是否对supercat描述的奇怪行为有任何见解? - ean5533
3
第一次调用 GetEnumerator() 返回的是同一个引用;之后,它会创建分开的副本。这样做的想法是在只迭代一次的常见情况下只创建一个对象。 - Jon Skeet
1
@TamusJRoyce:那是哪个枚举器类?如果序列是非空值类型,怎么办?或者如果它是一个以 null 作为第一个值的序列呢? - Jon Skeet
显示剩余5条评论

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