【问题标题】:What is the best way to pass a stream around传递流的最佳方法是什么
【发布时间】:2013-12-29 12:42:06
【问题描述】:

我尝试将流作为参数传递,但我不确定哪种方式是“最好的”,所以想听听您对我的代码示例的意见/建议

我个人更喜欢选项 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);
    }
}

【问题讨论】:

    标签: c# stream arguments


    【解决方案1】:

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

    关于您的实施,我建议您使用选项 #2:

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

    关于对象的生命周期,我认为:

    • 实现应该在处理调用Dispose的方式上保持一致
    • 如果 IStreamHandler 不调用 Dispose,您的解决方案将更加灵活(现在您可以像在 Unix 管道中一样将处理程序链接在一起)

    第三方解决方案

    构建管道解决方案可能很有趣,但同样值得注意的是,市场上有现有产品:

    附加说明

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

    void Process(Stream stream);
    

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

    【讨论】:

    • 谢谢!我将阅读该设计模式。但现在;您能否详细说明您认为谁有责任关闭处置。我应该使用选项2_1或选项2_2所示的界面吗?
    • 我会选择:DoOption2_2。更重要的是:(1) 与你的实现保持一致,(2) 确保在意外抛出异常的情况下释放所有资源。
    【解决方案2】:

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

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

    选项 3 和 2_1 依赖不同的对象来清理 SomeTestClass 拥有的资源 - 可能无法满足此预期。

    选项 1 只是将流的内容复制到另一个流 - 我认为这样做没有任何好处。

    【讨论】:

    • 选项 3 只是使用“MemoryStream”作为一种能够将流转换为字节数组的方法。 - 所以这不是将流内容解析到另一个流的问题。
    • 所以要清楚;如果你被要求暗示“void HandleStream(Stream stream);”你会假设你的实现没有责任关闭/处置流?
    • @JakobDyrby - 选项 3 仍然是从 Stream 读取,写入 MemoryStream,然后转换为 byte[]。为什么不直接从Stream 读取并使用缓冲区呢?
    • @JakobDyrby:是的。如果一次性资源(不仅仅是流)作为参数传递给我的方法,我不会认为我应该关闭它。事实上,我会说关闭它是我的责任。我不知道谁在给我打电话,我不知道打电话给我的人是否需要进一步访问该信息流 - 所以我有义务让它保持打开状态。
    • @JakobDyrby 根据经验,关键字是ownership。拥有IDisposable 实例的人有责任关闭它。
    猜你喜欢
    • 2010-10-10
    • 1970-01-01
    • 2021-08-31
    • 2019-02-04
    • 1970-01-01
    • 1970-01-01
    • 2017-03-26
    • 1970-01-01
    • 2020-11-17
    相关资源
    最近更新 更多