【问题标题】:When does File.ReadLines free resourcesFile.ReadLines 什么时候释放资源
【发布时间】:2011-03-23 16:44:30
【问题描述】:

在使用 C# 处理文件时,我习惯于考虑释放相关资源。通常这是一个 using 语句,除非它是一个单一的便捷方法 比如 File.ReadAllLines,它将为我打开和关闭文件。

.Net 4.0 引入了便捷方法 File.ReadLines。这将返回一个 IEnumerable,并被认为是一种更有效的文件处理方式——它避免了将整个文件存储在内存中。为此,我假设枚举器中有一些延迟执行逻辑。

显然,由于此方法返回的是 IEnumerable,而不是 IDisposable,因此我无法接受 using 语句的直觉反应。

我的问题是: 考虑到这一点,使用这种方法释放资源是否有任何陷阱?

调用这个方法是否意味着关联文件锁的释放是不确定的?

【问题讨论】:

    标签: c# .net-4.0


    【解决方案1】:

    IEnumerable 不从 IDisposable 继承,因为通常,实现它的类只为您提供可枚举的承诺,它实际上还没有做任何需要处置的事情。

    但是,当您枚举它时,您首先通过调用IEnumerable.GetEnumerator 方法检索IEnumerator,通常,您返回的底层对象确实实现IDisposable

    foreach的实现方式类似这样:

    var enumerator = enumerable.GetEnumerator();
    try
    {
        // enumerate
    }
    finally
    {
        IDisposable disposable = enumerator as IDisposable;
        if (disposable != null)
            disposable.Dispose();
    }
    

    这样,如果对象确实实现了IDisposable,它将被丢弃。对于File.ReadLines,文件在您开始枚举之前不会真正打开,因此您从File.ReadLines 获得的对象不需要处理,但您获得的枚举器需要处理。

    正如 cmets 所指出的,IEnumerator 不继承自 IDisposable,尽管许多典型实现确实如此,而通用 IEnumerator<T> 确实继承 IDisposable

    【讨论】:

    • 实际上,IEnumerator 并没有实现 IDisposable。但是 IEnumerator 的许多实现确实实现了 IDisposable,foreach 循环和 LINQ 扩展方法都执行as 强制转换以查看枚举器是否实现了 IDisposable,如果是,它们调用 Dispose。
    • 更正:非泛型 IEnumerator 不实现 IDisposable,但泛型 (IEnumerator<T>) 可以。
    • 啊,你是对的。啊,愚蠢的错误,让我编辑答案。
    【解决方案2】:

    +1 表示 Lasse 的回答。

    特别是对于枚举器调用.MoveNext()File.ReadLines,内部TextReader 将在遇到EOF 或发生故障时被释放。

    private bool MoveNext()
    {
        bool flag;
        try
        {
            switch (this.<>1__state)
            {
                case 0:
                    this.<>1__state = -1;
                    this.<>7__wrap2 = this.reader;
                    this.<>1__state = 1;
                    this.<line>5__1 = null;
                    goto Label_005C;
    
                case 2:
                    this.<>1__state = 1;
                    goto Label_005C;
    
                default:
                    goto Label_0078;
            }
        Label_003E:
            this.<>2__current = this.<line>5__1;
            this.<>1__state = 2;
            return true;
        Label_005C:
            if ((this.<line>5__1 = this.reader.ReadLine()) != null)
            {
                goto Label_003E;
            }
            this.<>m__Finally3(); // Disposal at end of file.
        Label_0078:
            flag = false;
        }
        fault
        {
            this.System.IDisposable.Dispose(); // Disposal due to fault.
        }
        return flag;
    }
    

    【讨论】:

      【解决方案3】:

      只是为了更新:

      在.net 5 中,它返回的IEnumerable 确实实现了IDisposable,您可能需要在转换后调用Dispose。但如果您阅读到文件末尾,它会自行处理。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-13
        • 2021-12-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多