提问人:ruedi 提问时间:8/5/2014 最后编辑:ruedi 更新时间:4/27/2023 访问量:19389
使用 IDisposable 清理 Excel 互操作对象
Clean up Excel Interop Objects with IDisposable
问:
在我的公司中,发布 Excel 互操作对象的常用方法是使用以下方式:IDisposable
Public Sub Dispose() Implements IDisposable.Dispose
If Not bolDisposed Then
Finalize()
System.GC.SuppressFinalize(Me)
End If
End Sub
Protected Overrides Sub Finalize()
_xlApp = Nothing
bolDisposed = True
MyBase.Finalize()
End Sub
其中按以下方式在构造函数中创建:_xlApp
Try
_xlApp = CType(GetObject(, "Excel.Application"), Excel.Application)
Catch e As Exception
_xlApp = CType(CreateObject("Excel.Application"), Excel.Application)
End Try
客户端使用 执行有关 excel 互操作对象的代码。using-statement
我们完全避免使用两点法则。现在我开始研究如何发布 (Excel) 互操作对象,我发现几乎所有关于它的讨论,例如如何正确清理 excel 互操作对象或释放 Excel 对象,它们都主要使用,它们都没有使用界面。Marshal.ReleaseComObject()
IDisposable
我的问题是:使用 interace 发布 excel 互操作对象有什么缺点吗?如果是这样,这些缺点是什么。IDisposable
答:
使用 IDisposable Interace 有什么缺点吗
当然,它绝对什么也做不了。使用使用或调用 Dispose() 从来都不是将变量设置为 Nothing 的合适方法。这就是您的代码所做的一切。
我们完全避免使用两点法则。
随意继续忽略它,这是无稽之谈,只会引起悲伤。博客作者的隐含断言是,这样做会迫使程序员使用变量来存储 xlApp.Workbooks 的值。因此,他以后有机会不忘记调用 releaseObject()。但是,还有更多语句生成不使用点的接口引用。类似于 Range(x,y) 之类的东西,那里有一个隐藏的 Range 对象引用,你永远不会看到。还必须存储它们只会产生令人难以置信的复杂代码。
仅仅忽略一个就足以完全无法完成工作。完全不可能调试。这是 C 程序员必须编写的代码。大型 C 程序经常失败,经常泄漏内存,它们的程序员花费大量时间寻找这些泄漏。当然不是 .NET 方式,它有一个垃圾回收器来自动执行此操作。它永远不会出错。
麻烦的是,它在处理工作时有点慢。非常有意为之。除了在这种代码中,没有人会注意到这一点。您可以看到垃圾回收器未运行,您仍然看到 Office 程序正在运行。当你写 xlapp 时,它并没有退出。Quit(),它仍然存在于任务管理器的“进程”选项卡中。他们想要的是,当他们这么说时,它就会退出。
这在 .NET 中是很有可能的,你当然可以强制 GC 完成工作:
GC.Collect()
GC.WaitForPendingFinalizers()
砰,每个 Excel 对象引用都会自动释放。无需自己存储这些对象引用并显式调用 Marshal.ReleaseComObject(),CLR 会为你执行此操作。而且它永远不会出错,它不使用或不需要“两点法则”,并且可以轻松找到那些隐藏的接口引用。
然而,重要的是你把这段代码放在哪里。大多数程序员把它放在错误的地方,用与使用这些 Excel 界面相同的方法。这很好,但在调试代码时不起作用,这是本答案中解释的一个怪癖。在博客作者的代码中执行此操作的正确方法是将代码移动到一个小的帮助程序方法中,我们将其称为 DoExcelThing()。喜欢这个:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
DoExcelThing()
GC.Collect()
GC.WaitForPendingFinalizers()
'' Excel.exe no longer running anymore at this point
End Sub
请记住,这实际上只是一个调试工件。程序员只是讨厌必须使用任务管理器来杀死僵尸Excel.exe实例。当他们停止调试器时僵尸化,阻止程序正常退出并收集垃圾。这是正常的。当您的程序因任何原因在生产中死亡时,也会发生这种情况。把你的精力放在它所属的地方,从你的代码中去除错误,这样你的程序就不会死掉。GC 不需要比这更多的帮助。
评论
Dispose
GC.Collect()
如果您正在寻找一种更清洁的方式,您可以使用 Koogra。它没有太多额外的开销(只需在引用中包含两个 DLL),并且不必处理隐式垃圾回收。
下面的代码是从 excel 文件中读取 10 行 10 列所需的全部代码,没有留下任何 EXCEL32 或 Excel.exe 进程。一个月前我遇到了这个问题,并写了关于如何做到这一点的说明。比直接处理 Excel Interop 要容易得多,也更干净。
Koogra.IWorkbook workbook = Koogra.WorkbookFactory.GetExcel2007Reader("MyExcelFile.xlsx");
Net.SourceForge.Koogra.IWorksheet worksheet = workbook.Worksheets.GetWorksheetByName("Sheet1");
//This will invididually print out to the Console the columns A-J (10 columns) for rows 1-10.
for (uint rowIndex = 1; rowIndex <= 10; rowIndex++)
{
for (uint columnIndex = 1; columnIndex <= 10; columnIndex++)
{
Console.WriteLine(worksheet.Rows.GetRow(rowIndex).GetCell(columnIndex).GetFormattedValue());
}
}
评论