Когда File.ReadLines освобождает ресурсы? - PullRequest
13 голосов
/ 31 июля 2010

При работе с файлами в C # я вынужден думать об освобождении связанных ресурсов. Обычно это оператор использования, если это не удобный метод как File.ReadAllLines, который откроет и закроет файл для меня.

.Net 4.0 представил удобный метод File.ReadLines. Это возвращает IEnumerable и выставляется счет как более эффективный способ обработки файла - он позволяет избежать сохранения всего файла в памяти. Для этого я предполагаю, что в перечислителе есть некоторая логика отложенного выполнения.

Очевидно, что поскольку этот метод возвращает IEnumerable, а не IDisposable, я не могу согласиться со своей внутренней реакцией на использование выражения.

Мои вопросы: Имея это в виду, есть ли какие-либо ошибки в освобождении ресурсов с этим методом?

Означает ли вызов этого метода, что освобождение связанных блокировок файлов недетерминировано?

Ответы [ 2 ]

17 голосов
/ 31 июля 2010

IEnumerable не наследуется от IDisposable, потому что обычно класс, который его реализует, дает вам обещание быть перечисляемым, на самом деле он еще ничего не сделал, что гарантирует удаление.

Однако, когда вы перечисляете его, вы сначала извлекаете IEnumerator, вызывая метод IEnumerable.GetEnumerator, и обычно возвращаемый базовый объект выполняет реализацию IDisposable.

Способ реализации foreach аналогичен следующему:

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

Таким образом, если объект действительно реализует IDisposable, он будет удален. Для File.ReadLines файл на самом деле не открывается, пока вы не начнете перечислять его, поэтому объект, который вы получаете из File.ReadLines, не нуждается в удалении, но получаемый вами перечислитель делает это.

Как показывают комментарии, IEnumerator не наследуется от IDisposable, хотя многие типичные реализации это делают, тогда как универсальный IEnumerator<T> наследует IDisposable.

5 голосов
/ 31 июля 2010

+ 1 за ответ Лассе.

Особенно для File.ReadLines, когда перечислитель вызывает .MoveNext(), внутренний 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;
}
...