我是否需要考虑处置我使用的任何IEnumerable <T>?


70

它最近向我指出,各种LINQ的扩展方法(如WhereSelect等)返回IEnumerable<T>,恰巧也有IDisposable。以下评估为True

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

我是否需要处理Where表达式的结果?

每当我调用返回的方法时,完成操作后IEnumerable<T>,我是否(潜在地)承担对处置的责任?

Answers:


85

不,您不必为此担心。

它们返回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
因此,只要我不显式使用枚举数,就不需要处置?
罗布

1
这是有趣的是,呼吁Dispose在这已经一个迭代符GetEnumerator()至少调用一次会作废第一调用返回的枚举,而不是由后续调用返回的任何统计员; 调用Dispose第一之前GetEnumerator()调用没有效果。
2012年

@JonSkeet您是否对以下描述的奇怪行为有任何见解supercat
ean5533

2
@ ean5533:的第一个调用GetEnumerator()返回对相同引用的引用;之后,它将创建单独的副本。这个想法是在仅迭代一次的常见情况下仅创建一个对象。
乔恩·斯基特

不用在Enumerator.MoveNext()上检查true,而是使用Enumerator.FirstOrDefault()!= null。如果框架需要,这封装了处置。使用支持多线程的集合时,这是安全的。
TamusJRoyce 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.