我有一个执行PDF文件操作的第三方组件。每当我需要执行操作时,我都会从文档存储(数据库,SharePoint,文件系统等)中检索PDF文档。为了使事情稍微一致,我将PDF文档作为byte[]
传递。
此第三方组件需要MemoryStream[]
(MemoryStream
数组)作为我需要使用的主要方法之一的参数。
我正在尝试将此功能包装在我自己的组件中,以便我可以将此功能用于我的应用程序中的许多区域。我基本上提出了以下几点:
public class PdfDocumentManipulator : IDisposable
{
List<MemoryStream> pdfDocumentStreams = new List<MemoryStream>();
public void AddFileToManipulate(byte[] pdfDocument)
{
using (MemoryStream stream = new MemoryStream(pdfDocument))
{
pdfDocumentStreams.Add(stream);
}
}
public byte[] ManipulatePdfDocuments()
{
byte[] outputBytes = null;
using (MemoryStream outputStream = new MemoryStream())
{
ThirdPartyComponent component = new ThirdPartyComponent();
component.Manipuate(this.pdfDocumentStreams.ToArray(), outputStream);
//move to begining
outputStream.Seek(0, SeekOrigin.Begin);
//convert the memory stream to a byte array
outputBytes = outputStream.ToArray();
}
return outputBytes;
}
#region IDisposable Members
public void Dispose()
{
for (int i = this.pdfDocumentStreams.Count - 1; i >= 0; i--)
{
MemoryStream stream = this.pdfDocumentStreams[i];
this.pdfDocumentStreams.RemoveAt(i);
stream.Dispose();
}
}
#endregion
}
我的“包装器”的调用代码如下所示:
byte[] manipulatedResult = null;
using (PdfDocumentManipulator manipulator = new PdfDocumentManipulator())
{
manipulator.AddFileToManipulate(file1bytes);
manipulator.AddFileToManipulate(file2bytes);
manipulatedResult = manipulator.Manipulate();
}
关于上述问题的一些问题:
using
方法中的AddFileToManipulate()
子句是多余的还是不必要的?Dispose()
方法中清理好了吗?MemoryStream
的“可接受”用法吗?我不是一次在内存中预期很多文件...可能总共1-10页PDF页面,每页大约200KB。应用程序旨在运行在支持ASP.NET站点的服务器上。感谢代码审核:)
答案 0 :(得分:4)
AddFileToManipulate吓到了我。
public void AddFileToManipulate(byte[] pdfDocument)
{
using (MemoryStream stream = new MemoryStream(pdfDocument))
{
pdfDocumentStreams.Add(stream);
}
}
此代码正在向pdfDocumentStream列表中添加已处理的流。相反,您应该使用以下方法添加流:
pdfDocumentStreams.Add(new MemoryStream(pdfDocument));
并在Dispose方法中处理它。
此外,您应该考虑实现终结器,以确保在有人忘记处置顶级对象的情况下处理内容。
答案 1 :(得分:3)
更糟糕的是,它具有破坏性。你基本上是在关闭你的内存流之前关闭你的内存流。有关详细信息,请参阅其他答案,但基本上,最后处理,但不是任何其他时间。每次使用对象都会导致Dispose发生在块的末尾,即使对象通过方法“传递”到其他对象。
是的,但是你的生活比现在更困难。试试这个:
foreach (var stream in this.pdfDocumentStreams)
{
stream.Dispose();
}
this.pdfDocumentStreams.Clear();
这也很有效,而且更简单。处置对象不会删除它 - 它只是告诉它释放它的内部非托管资源。以这种方式调用对象的处理很好 - 对象在集合中保持未收集状态。您可以执行此操作,然后一次性清除列表。
这取决于您的情况。只有你可以确定在内存中使用这些文件的开销是否会导致问题。不过,这将是一个相当重量级的对象,所以我会仔细使用它。
实施终结者。无论何时实施IDisposable都是个好主意。此外,您应该将Dispose实现重新设计为标准实现,或将您的类标记为已密封。有关如何执行此操作的详细信息,see this article.特别是,您应该将一个声明为protected virtual void Dispose(bool disposing)
的方法设置为Dispose方法和终结器都调用。
答案 2 :(得分:2)
在我看来,你误解了使用的功能。
替换
只是语法糖MemoryStream ms;
try
{
ms = new MemoryStream();
}
finally
{
ms.Dispose();
}
您在AddFileToManipulate中的使用是多余的。我在PdfDocumentManipulator的构造函数中设置了内存流列表,然后在所有内存流上使用PdfDocumentManipulator的dispose方法调用dispose。
答案 3 :(得分:2)
旁注。这看起来好像需要一个扩展方法。
public static void DisposeAll<T>(this IEnumerable<T> enumerable)
where T : IDisposable {
foreach ( var cur in enumerable ) {
cur.Dispose();
}
}
现在您的Dispose方法变为
public void Dispose() {
pdfDocumentStreams.Reverse().DisposeAll();
pdfDocumentStreams.Clear();
}
修改强>
您不需要3.5框架才能拥有扩展方法。他们将很乐意在3.0编译器上工作,目标是2.0
http://blogs.msdn.com/jaredpar/archive/2007/11/16/extension-methods-without-3-5-framework.aspx