IDisposable в Конструкторе Цепи - PullRequest
1 голос
/ 12 апреля 2019

У меня есть следующие два конструктора:

public Source(FileStream fileStream) {
    // Stuff
}

public Source(String fileName) : this(new FileStream(fileName, FileMode.Open)) {
    // Nothing, the other constructor does the work
}

Проблема со вторым конструктором довольно очевидна, FileStream создается и используется, но не удаляется.Поскольку он находится внутри цепочки конструктора, блок using невозможен.Я не могу переместить new FileStream() в тело конструктора, потому что тогда он будет находиться в блоке using, но логика другого конструктора не сможет быть вызвана.Я не могу извлечь эту логику, потому что она изменяет поля readonlyмог бы дублировать логику в каждом конструкторе, но это, очевидно, не очень хорошее решение.

Я действительно предпочел бы сохранить синтаксический сахар, предоставляемый вторым конструктором.Как я могу лучше всего это сделать?Или это просто плохая идея?

Ответы [ 2 ]

4 голосов
/ 12 апреля 2019

Взгляните на реализацию StreamReader, которая имеет два типа ctors:

public StreamReader(Stream stream)
      : this(stream, true)
    {
    }

public StreamReader(string path)
      : this(path, true)
    {
    }

Внутренне они оба вызывают один и тот же метод Init с параметром leaveOpen, который установлен на true для первого ctor и на false для второго ctor и на основе этого параметра получает Stream (или нет) утилизировать.

Так что вы можете сделать что-то вроде этого:

public class Source : IDisposable
{
    private readonly Stream _stream;
    private readonly bool _leaveOpen;

    private Source(Stream stream, bool leaveOpen)
    {
        _stream = stream;
        _leaveOpen = leaveOpen;
    }

    public Source(FileStream fileStream) : this(fileStream, true)
    {

    }

    public Source(string fileName) : this(new FileStream(fileName, FileMode.Open), false)
    {

    }

    public void Dispose()
    {
        if (!_leaveOpen)
        {
            _stream?.Dispose();
        }
    }
}
4 голосов
/ 12 апреля 2019

Я не совсем уверен, что мешает вам избавиться от него в конструкторе, который принимает FileStream:

public Source(FileStream fileStream) {
    try
    {
        // Stuff
    }
    finally
    {
        fileStream.Dispose();
    }
}

public Source(String fileName) : this(new FileStream(fileName, FileMode.Open)) {
    // Nothing, the other constructor does the work
}

Если это потому, что вы хотите сохранить Stream живым для абонентовFileStream конструктор, вы можете добавить третий private конструктор:

public Source(FileStream fileStream): this(fileStream, disposeStream: false) {
    // Nothing, the other constructor does the work
}

public Source(String fileName) : this(new FileStream(fileName, FileMode.Open), disposeStream: true) {
    // Nothing, the other constructor does the work
}

private Source(FileStream fileStream, bool disposeStream) {
    try
    {
        // Stuff
    }
    finally
    {
        if (disposeStream)
        {
            fileStream.Dispose();
        }
    }
}
...