Почему поток удаляется, когда его писатель удаляется? - PullRequest
9 голосов
/ 05 декабря 2010

Рассмотрим следующий код:

using (var ms = new MemoryStream())
{
    using(var writer = BinaryWriter(ms))
    {
        writer.Write(/*something*/);
        writer.Flush();
    }

    Assert.That(ms.Length > 0); // Throws ObjectDisposedException
}

С одной стороны, одноразовый предмет должен распоряжаться своими ресурсами;Я понимаю, что, с другой стороны, объект не создавал и не владеет этим ресурсом, он был предоставлен -> вызывающий код должен взять на себя ответственность за это ... нет?

Я не могу думать о каких-либо других подобных ситуациях, но является ли это последовательной схемой в структуре для любого класса, получающего одноразовые объекты, чтобы утилизировать их по своему усмотрению?

Ответы [ 4 ]

13 голосов
/ 05 декабря 2010

Существует неявное предположение, что у вас будет только один писатель на поток, поэтому писатель для удобства принимает на себя владение потоком - тогда вам, безусловно, придется очистить одну вещь.

Но я согласен; это не всегда так, и часто неудобно. Некоторые реализации (например, DeflateStream, GZipStream) позволяют выбирать. В противном случае единственная реальная возможность - ввести фиктивный поток между записывающим и основным потоком; IIRC есть библиотека NonClosingStreamWrapper в библиотеке "MiscUtil" Джона Скита, которая делает именно это: http://www.yoda.arachsys.com/csharp/miscutil/

Использование будет примерно таким:

using (var ms = new MemoryStream())
{
    using(var noClose = new NonClosingStreamWrapper(ms))
    using(var writer = BinaryWriter(noClose))
    {
        writer.Write(/*something*/);
        writer.Flush();
    }

    Assert.That(ms.Length > 0);
}
4 голосов
/ 05 декабря 2010

Я полностью согласен с вами.Это не последовательное поведение, но так оно и было реализовано.Есть комментарии в конце документации об этом поведении, которое не очень интуитивно понятно.Все авторы потоков просто берут на себя ответственность за основной поток и удаляют его.Лично я всегда вкладываю свое using утверждение так:

using (var ms = new MemoryStream())
using(var writer = BinaryWriter(ms))
{
    writer.Write(/*something*/);
}

, чтобы код, подобный тому, который вы вставили в Assert, не был написан.

0 голосов
/ 21 сентября 2011

Я предлагаю этот класс оболочки:

public class BetterStreamWriter : StreamWriter
{
    private readonly bool _itShouldDisposeStream;

    public BetterStreamWriter(string filepath)
        :base(filepath)
    {
        _itShouldDisposeStream = true;
    }

    public BetterStreamWriter(Stream stream)
        : base(stream)
    {
        _itShouldDisposeStream = false;
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing && _itShouldDisposeStream);
    }
}

Объекты не должны располагать вещами, которые они не создавали. Если это средство записи файлового потока, его следует утилизировать. Если это внешний поток, он не должен.

Во-первых, не нужно было реализовывать путь открытия файла. Это нарушает принцип единой ответственности, так как объект управляет записью и временем жизни файла.

0 голосов
/ 07 декабря 2010

Правильнее всего было бы сделать так, чтобы параметр конструктора для потокового редактора указывал, должен ли поток располагаться, когда используется конструктор.Учитывая, что Microsoft этого не сделала, было бы неплохо определить класс NonDisposingStream (Of T as Stream), который оборачивает поток, но не передает вызов Dispose для обернутого потока.Затем можно передать новый NonDisposingStream в конструктор StreamWriter, и базовый поток будет безопасен для удаления (необходимо, конечно, избавиться от потока самостоятельно).

Наличие объекта, которыйможет распоряжаться переданным объектом полезно.Хотя такое поведение не совпадает с обычной схемой, в которой создатель объекта обращается с его удалением, часто бывают ситуации, когда создатель объекта не знает, как долго этот объект будет фактически необходим.Например, можно ожидать, что метод создаст новый StreamWriter, который использует новый поток.Владелец StreamWriter будет знать, когда он должен быть утилизирован, но может не знать о существовании внутреннего потока.Создатель внутреннего потока не будет знать, как долго будет использоваться внешний StreamWriter.«Передача» права собственности на поток StreamWriter решает проблему утилизации в этом конкретном (общем) случае.

...