什么是传递流的最佳方式?

15

我尝试将流作为参数传递,但不确定哪种方式是“最佳”的,因此希望听取您对我的代码示例的意见/建议。

个人而言,我更喜欢选项3,但我从未在任何其他地方看到过这样做。

选项1适用于小型流(和已知大小的流)。

选项2_12_2总是让“处理程序”怀疑谁有负责释放/关闭的责任。

public interface ISomeStreamHandler
{
    // Option 1
    void HandleStream(byte[] streamBytes);

    // Option 2
    void HandleStream(Stream stream);

    // Option 3
    void HandleStream(Func<Stream> openStream);
}

public interface IStreamProducer
{
    Stream GetStream();
}

public class SomeTestClass
{
    private readonly ISomeStreamHandler _streamHandler;
    private readonly IStreamProducer _streamProducer;

    public SomeTestClass(ISomeStreamHandler streamHandler, IStreamProducer streamProducer)
    {
        _streamHandler = streamHandler;
        _streamProducer = streamProducer;
    }

    public void DoOption1()
    {
        var buffer = new byte[16 * 1024];
        using (var input = _streamProducer.GetStream())
        {
            using (var ms = new MemoryStream())
            {
                int read;
                while ((read = input.Read(buffer, 0, buffer.Length)) > 0) 
                {
                    ms.Write(buffer, 0, read);
                }
                _streamHandler.HandleStream(ms.ToArray());
            }
        }
    }

    public void DoOption2_1()
    {
        _streamHandler.HandleStream(_streamProducer.GetStream());
    }

    public void DoOption2_2()
    {
        using (var stream = _streamProducer.GetStream())
        {
            _streamHandler.HandleStream(stream);    
        }
    }

    public void DoOption3()
    {
        _streamHandler.HandleStream(_streamProducer.GetStream);
    }
}
2个回答

3

选项2_2是处理一次性资源的标准方式。

SomeTestClass实例向生产者请求一个流,然后 SomeTestClass 拥有该流并负责清理。

选项3和2_1依赖于另一个对象来清理 SomeTestClass 拥有的资源-这种期望可能无法满足。

选项1只是将流的内容复制到另一个流中-我看不出任何好处。


选项3只是使用“MemoryStream”作为一种将流转换为字节数组的手段。因此,这不是将流内容解析到另一个流的问题。 - Jakob Dyrby
@JakobDyrby - 仍然,选项3是从Stream读取,写入MemoryStream,并转换为byte[]。为什么不直接从Stream中读取并使用缓冲区呢? - dcastro
@JakobDyrby:是的。如果我的方法作为参数传递了一个可丢弃的资源(不仅仅是流),我不会假设我应该关闭它。事实上,我会说这是我的责任不要关闭它。我不知道是谁在调用我,我也不知道调用我的人是否需要进一步访问该流——所以我有义务将其保持打开状态。 - dcastro
好的,在我的情况下 - 誰實際“擁有”IDisposable(流)。換句話說,什麼定義了所有權? - Jakob Dyrby
我不希望“SomeTestClass”拥有该流 - 我只想让该类提供访问流的方式。 "SomeTestClass" 对它没有用处。这就是为什么我更喜欢选项3。为什么这不完全可以呢? - Jakob Dyrby
显示剩余4条评论

1

您可能没有意识到,但您正在尝试实现“管道设计模式”。作为起点,请考虑查看以下内容:

关于您的实现,我建议您选择选项#2:

public interface IStreamHandler
{
    void Process(Stream stream);
}

关于对象生命周期,我认为:
- 实现应该在处理调用Dispose时保持一致 - 如果IStreamHandler没有调用Dispose,你的解决方案将更加灵活(现在可以像Unix管道一样链接处理程序)
第三方解决方案
构建管道解决方案可能很有趣,但也值得注意的是市场上已经存在现有产品:
- Yahoo:Pipes - Microsoft:BizTalk - IBM:Cast Iron - StackOverflow:替代Yahoo Pipes 附加说明

您提出的选项2存在与设计相关的问题:

void Process(Stream stream);

Unix Pipes中,您可以通过将一个程序的输出作为另一个程序的输入来将多个应用程序链接在一起。如果您使用 Option 2 构建类似的解决方案,并且您正在使用多个处理程序并且您的数据 Stream 是仅向前的(即 stream.CanSeek = False ),则会遇到问题。

谢谢!我会研究一下这个设计模式。但现在,你能否详细说明一下你认为谁有关闭处理的责任?我应该使用选项2_1中显示的接口还是选项2_2中的接口? - Jakob Dyrby
我会选择:DoOption2_2。更重要的是:**(1)** 在你的实现中保持一致,和 (2) 确保在意外抛出异常时释放所有资源。 - Pressacco

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接