【问题标题】:Do I need to consider disposing of any IEnumerable<T> I use?我是否需要考虑处置我使用的任何 IEnumerable<T>?
【发布时间】:2012-11-07 16:59:48
【问题描述】:

最近有人向我指出,各种 Linq 扩展方法(例如 WhereSelect 等)返回的 IEnumerable&lt;T&gt; 也恰好是 IDisposable。以下计算结果为True

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

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

每当我调用返回IEnumerable&lt;T&gt; 的方法时,我是否(可能)在完成调用 dispose 时承担责任?

【问题讨论】:

    标签: c# linq ienumerable dispose


    【解决方案1】:

    不,你不必担心这个。

    他们返回 IDisposable 实现的事实是一个实现细节 - 这是因为 C# 编译器的 Microsoft 实现中的迭代器块碰巧创建了一个实现 IEnumerable&lt;T&gt;IEnumerator&lt;T&gt;。后者扩展了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&lt;T&gt; 实现IDisposable 的事实。所以任何时候你明确地迭代,你都应该正确地处理它。例如,如果您想迭代某些东西并确保您将始终拥有 a 值,您可能会使用类似:

    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 无效。
    • @JonSkeet 你对supercat描述的奇怪行为有什么见解吗?
    • @ean5533:对GetEnumerator()的第一次调用返回对同一个引用的引用;此后,它将创建单独的副本。这个想法是只在迭代一次的常见情况下创建一个对象。
    • 不要在 Enumerator.MoveNext() 上检查 true,而是使用 Enumerator.FirstOrDefault() != null。如果需要,这封装了对框架的配置。使用多线程感知集合时这是安全的。
    猜你喜欢
    • 1970-01-01
    • 2019-09-09
    • 1970-01-01
    • 1970-01-01
    • 2023-02-08
    • 2012-12-15
    • 1970-01-01
    • 2020-08-21
    • 2020-05-03
    相关资源
    最近更新 更多