为什么当它的 writer 被释放时,流也会被释放?

发布于 2024-10-06 06:22:20 字数 410 浏览 0 评论 0原文

考虑以下代码:

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

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

一方面,一次性对象应该处置其资源;另一方面,我明白了,但另一方面,该对象没有创建并且不拥有该资源,它是提供的 ->调用代码应该对此负责...不是吗?

我想不出任何其他类似的情况,但是对于任何接收一次性对象并自行处理它们的类来说,这是框架中一致的模式吗?

Consider the following code:

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

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

On the one hand, a disposable object should dispose of it's resources; I get that, but on the other hand, the object didn't create and doesn't own this resource, it was provided -> calling code should take responsibility for it... no?

I can't think of any other situations like this, but is it a consistent pattern in the framework for any class receiving disposable objects to dispose of them on its own dispose?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

自由范儿 2024-10-13 06:22:20

有一个隐含的假设,即每个流只有一个写入器,因此为了方便起见,写入器假设该流的所有权 - 然后您显然需要清理一件事。

但我同意;但这并不总是正确的,而且常常带来不便。某些实现(例如 DeflateStream、GZipStream)允许您进行选择。否则,唯一真正的选择是在编写器和底层流之间注入一个虚拟流; IIRC Jon Skeet 的“MiscUtil”库中有一个 NonClosingStreamWrapper 可以实现此目的: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);
}

There is an implicit assumption that you will only have one writer per stream, so the writer assumes ownership of the stream for convenience - you then obly have one thing to clean up.

But I agree; this is not always true, and often inconvenient. Some implementations (DeflateStream, GZipStream, for example) allow you to choose. Otherwise the only real option is to inject a dummy stream between the writer and the underlying stream; IIRC there is a NonClosingStreamWrapper in Jon Skeet's "MiscUtil" library that does exactly this: http://www.yoda.arachsys.com/csharp/miscutil/

Usage would be something like:

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);
}
溺孤伤于心 2024-10-13 06:22:20

我完全同意你的观点。这不是一致的行为,但它是如何实现的。文档末尾有关于此的注释行为不是很直观。所有流编写者都只是获得底层流的所有权并处置它。就我个人而言,我总是像这样嵌套我的 using 语句:

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

这样就不应该编写像您放入 Assert 中的代码。

I totally agree with you. This is not consistent behavior but it is how it has been implemented. There are comments at the end of the documentation about this behavior which is not very intuitive. All stream writers just take ownership of the underlying stream and dispose it. Personally I always nest my using statement like this:

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

so that a code like the one you put in the Assert shouldn't be written.

墨落成白 2024-10-13 06:22:20

正确的做法是让流编写器有一个构造函数参数,指示在构造函数释放时是否应释放流。鉴于 Microsoft 没有这样做,最好定义一个 NonDisposeStream(Of T as Stream) 类,该类包装流,但不将 Dispose 调用传递给包装的流。然后,可以将新的 NonDisposeStream 传递给 StreamWriter 的构造函数,并且底层流将不会被处置(当然,您自己处置该流是必要的)。

拥有一个可以处理传入对象的对象是很有用的。虽然这种行为与对象创建者处理其处置的通常模式并不相符,但在某些情况下,对象的创建者通常不知道该对象实际需要多长时间。例如,一个方法可能会创建一个使用新 Stream 的新 StreamWriter。 StreamWriter 的所有者将知道何时应该处置它,但可能不知道内部流的存在。内部流的创建者将不知道外部 StreamWriter 将使用多长时间。将流的所有权“移交给”StreamWriter 可以解决该特定(常见)情况下的处置问题。

The right thing to have done would have been to have a constructor parameter for the streamwriter indicate whether the stream should be disposed when the constructor is. Given that Microsoft didn't do that, it may be good to define a NonDisposingStream(Of T as Stream) class which wraps a stream but does not pass a Dispose call to the wrapped stream. One could then pass a new NonDisposingStream to the constructor of a StreamWriter, and the underlying stream would be safe from disposal (it would be necessary, of course, to dispose of the stream yourself).

Having an object which can dispose of a passed-in object is useful. While such behavior doesn't coincide with the usual pattern of an object's creator handling its disposal, there are often situations where an object's creator will have no idea how long the object will actually be needed. For example, a method may be expected to create a new StreamWriter which uses a new Stream. The owner of the StreamWriter will know when it should be disposed, but may not know of the inner stream's existence. The creator of the inner stream will have no idea how long the outer StreamWriter will be used. Having ownership of the stream "handed off" to the StreamWriter solves the disposal problem in that particular (common) case.

往昔成烟 2024-10-13 06:22:20

我建议这个包装类:

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);
    }
}

对象不应该处置它们没有实例化的东西。如果它是文件流编写器,则应该进行处理。如果它是外部流,则不应该。

一开始就不应该实现打开文件路径。这违反了单一职责原则,因为对象同时管理文件的写入和生命周期。

I propose this wrapper class:

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);
    }
}

Objects should not dispose stuff they didn't instantiate. If it's a file stream writer, it should dispose. If it's an external stream, it shouldn't.

Shouldn't have implemented opening file path in the first place. This violate single responsibility principle as the object both manages writing and lifetime of the file.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文