如何正确处置调用栈中创建的MemoryStream?
问题描述
我有一个使用MemoryStream的PrepareDocument函数,以及调用它的Render函数。该MemoryStream仅能在Render函数执行完成后进行处置。
public MyDocument PrepareDocument() { var documentImageStream = new MemoryStream(); ... document.registerImages(documentImageStream); return document; } public void Render() { var document = PrepareDocument(); var file = document.Render(); // Only at this point can the memory streams be disposed }
我需要处置该流,但不确定正确方式:是否要修改PrepareDocument的返回签名,返回包含待处置流的自定义类型?或是通过out参数输出待处置流列表?这两种方式在我看来都不够简洁,因为PrepareDocument本应仅负责生成并返回文档。是否有我未了解的using语句巧妙用法?
解决方案
方案1:让MyDocument实现IDisposable(推荐)
既然MyDocument内部持有了需要释放的MemoryStream资源,让它承担资源释放的责任是最符合.NET资源管理规范的,完全贴合谁持有谁释放的原则,同时不破坏PrepareDocument的单一职责:
- 修改
MyDocument实现IDisposable接口:
public class MyDocument : IDisposable { private List<MemoryStream> _registeredStreams = new List<MemoryStream>(); private bool _disposed = false; public void registerImages(MemoryStream stream) { _registeredStreams.Add(stream); } public object Render() { // 原有渲染逻辑 ... } // 实现IDisposable核心逻辑 public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (_disposed) return; if (disposing) { // 遍历释放所有注册的流 foreach (var stream in _registeredStreams) { stream.Dispose(); } _registeredStreams.Clear(); } _disposed = true; } // 可选终结器:防止未手动调用Dispose时的资源泄漏 ~MyDocument() { Dispose(false); } }
- 修改
Render函数,用using语句自动释放文档(连带内部流):
public void Render() { using (var document = PrepareDocument()) { var file = document.Render(); // 代码执行完毕后,using会自动调用document.Dispose(),释放所有注册的流 } }
方案2:返回文档与流的元组(简洁但职责稍模糊)
如果不想修改MyDocument的结构,可以用C#元组返回,比自定义类型更轻便:
public (MyDocument Document, MemoryStream ImageStream) PrepareDocument() { var documentImageStream = new MemoryStream(); ... document.registerImages(documentImageStream); return (document, documentImageStream); } public void Render() { var (document, stream) = PrepareDocument(); using (stream) { var file = document.Render(); } }
这种方式不需要改动文档类,但PrepareDocument的职责会扩展为“生成文档+返回关联流”,稍微偏离了原本的单一职责,但胜在实现简单。
方案3:使用out参数(不推荐)
out参数方式虽然可行,但会让函数签名臃肿,可读性差,仅在特殊场景下考虑:
public MyDocument PrepareDocument(out MemoryStream imageStream) { imageStream = new MemoryStream(); ... document.registerImages(imageStream); return document; } public void Render() { PrepareDocument(out var stream); using (stream) { var document = PrepareDocument(out stream); var file = document.Render(); } }
内容的提问来源于stack exchange,提问作者qkhanhpro
相关产品推荐
相关产品推荐

