【问题标题】:Who should dispose a Stream? The called method or the caller?谁应该处置 Stream?被调用的方法还是调用者?
【发布时间】:2017-03-25 18:13:39
【问题描述】:

我正在编写一个将数据导出到 Stream 的 API:

public interface IExporter<in T>
{
    Task ExportAsync(IEnumerable<T> inputs, Stream output);
}

可能各种IExporter 实现将使用TextWriter/StreamWriter 的形式,但我不想在接口上强制执行它。

使用StreamWriter 的主要问题是默认情况下它会关闭底层流(我知道有一个构造函数但它需要bufferSize,我可以继承StreamWriter 但我也不喜欢它)。

我应该在我的IExporter 实现中简单地“拥有”Stream(并通过处理我的StreamWriter 来处理它)还是有更好的方法来处理这个问题?

【问题讨论】:

  • @HansPassant 我不明白,我可以使用using 关键字还是我遗漏了什么?例如using (var stream = ...) { await _exporter.ExportAsync(..., stream); }?
  • 可以,但是要求客户端代码使用await。当它不工作时,它的工作会相当糟糕。只是没有必要,将假定 Export 方法消耗整个流,而不仅仅是其中的一部分。所以流对象完成后总是无用的,它还不如将它处理掉,现在它永远不会出错。否则 .NET 流总是转移所有权的核心原因。
  • @HansPassant,使用这种方法,我无法将多次导出到一个流中。如果我想按顺序写入数据怎么办?我不能假设 Export() 方法会消耗整个流。

标签: c# architecture


【解决方案1】:

调用者应对资源负责。被调用的方法没有任何信息(也不应该有任何信息)所提供的对象是否会被其他人使用。因此,处置提供给方法的任何对象是一个糟糕的决定(例如,如果该对象的生命周期由依赖注入容器管理怎么办?)

【讨论】:

  • 我同意,这就是我开始编写代码的方式。但是,通过在内部使用 StreamWriter 我不喜欢 not 处置它们以避免关闭 Stream。我是否应该简单地使用另一个构造函数msdn.microsoft.com/en-us/library/gg712853(v=vs.110).aspx(或使用不会关闭底层流的自定义 StreamWriter)?
  • @aghidini,这当然取决于你。如果您有任何自定义的Stream 派生实现,请使用它们。 leaveOpen 标志也是一个很好的解决方案。任何将实现您的接口的人都应该知道,他们不允许释放提供的流。无论如何async 没有问题:您可以在using 语句中使用await 方法,c# 编译器会为您管理。
  • 需要记住的一点是:之前的评论 “如果您有任何自定义的 Stream 派生实现,请使用它们。leaveOpen 标志也是一个很好的解决方案。任何将实现您的接口的人应该知道他们不允许释放提供的流”...尝试遵循SOLID Design Principles 并确保派生类型不会改变基类的预期行为。
【解决方案2】:

在这种情况下,我认为归结为个人喜好。一般来说,我会尽量遵循以下准则:

  1. 在应用程序的设计中保持一致
  2. 如果一个对象分配了一个资源(例如new),那么它也应该负责释放它(例如Dispose

补充阅读

【讨论】:

    猜你喜欢
    • 2014-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-22
    • 2021-04-13
    • 1970-01-01
    相关资源
    最近更新 更多