4

我有一个执行 PDF 文件操作的第三方组件。每当我需要执行操作时,我都会从文档存储(数据库、SharePoint、文件系统等)中检索 PDF 文档。为了使事情有点一致,我将 PDF 文档作为byte[].

这个第 3 方组件需要一个MemoryStream[]( MemoryStreamarray) 作为我需要使用的主要方法之一的参数。

我正在尝试将此功能包装在我自己的组件中,以便我可以将此功能用于我的应用程序中的许多区域。我基本上提出了以下几点:

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();
    }

关于上面的几个问题:

  1. 方法中的using子句是AddFileToManipulate()多余的和不必要的吗?
  2. Dispose()我在我的对象的方法中清理东西好吗?
  3. 这是“可接受的”用法MemoryStream吗?我预计内存中不会同时有很多文件......总共可能有 1-10 个 PDF 页,每页大约 200KB。设计为在支持 ASP.NET 站点的服务器上运行的应用程序。
  4. 有什么意见/建议吗?

感谢代码审查 :)

4

4 回答 4

5

AddFileToManipulate 让我害怕。

   public void AddFileToManipulate(byte[] pdfDocument)
   {
      using (MemoryStream stream = new MemoryStream(pdfDocument))
      {
         pdfDocumentStreams.Add(stream);
      }
   }

此代码将已处置的流添加到您的 pdfDocumentStream 列表中。相反,您应该使用以下方法简单地添加流:

   pdfDocumentStreams.Add(new MemoryStream(pdfDocument));

并在 Dispose 方法中处理它。

此外,您应该考虑实现终结器以确保在有人忘记处置顶级对象的情况下处置东西。

于 2009-03-19T02:35:54.830 回答
3
  1. AddFileToManipulate() 方法中的 using 子句是多余且不必要的吗?

更糟糕的是,它具有破坏性。您基本上是在添加内存流之前关闭它。有关详细信息,请参阅其他答案,但基本上,在最后处理,但不是任何其他时间。每次与对象一起使用都会导致在块的末尾发生 Dispose,即使该对象通过方法“传递”给其他对象也是如此。

  1. 我在我的对象的 Dispose() 方法中清理东西好吗?

是的,但你让生活变得比需要的更困难。试试这个:

foreach (var stream in this.pdfDocumentStreams)
{
    stream.Dispose();
}
this.pdfDocumentStreams.Clear();

这同样有效,而且更简单。释放一个对象并不会删除它——它只是告诉它释放它的内部非托管资源。以这种方式对对象调用 dispose 很好 - 对象在集合中保持未被收集。您可以执行此操作,然后一次性清除列表。

  1. 这是 MemoryStream 的“可接受”用法吗?我预计内存中不会同时有很多文件......总共可能有 1-10 个 PDF 页,每页大约 200KB。设计为在支持 ASP.NET 站点的服务器上运行的应用程序。

这取决于你的情况。只有您可以确定将这些文件放在内存中的开销是否会给您带来问题。不过,这将是一个相当重的物体,所以我会小心使用它。

  1. 有什么意见/建议吗?

实现终结器。每当您实现 IDisposable 时,这都是一个好主意。此外,您应该将您的 Dispose 实现重新设计为标准实现,或者将您的类标记为密封的。有关如何执行此操作的详细信息,请参阅本文。 特别是,您应该有一个方法声明为protected virtual void Dispose(bool disposing)您的 Dispose 方法和您的终结器都调用。

于 2009-03-19T07:32:18.847 回答
2

在我看来,您误解了 Using 的作用。

它只是替换的语法糖

MemoryStream ms;
try
{
ms = new MemoryStream();
}
finally
{
ms.Dispose();
}

您在 AddFileToManipulate 中的使用是多余的。我会在 PdfDocumentManipulator 的构造函数中设置内存流列表,然后在所有内存流上调用 PdfDocumentManipulator 的 dispose 方法调用 dispose。

于 2009-03-19T02:00:04.510 回答
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

于 2009-03-19T02:27:55.083 回答