我们访问的Excel对象是COM对象。对于COM对象(也包括接口),每次对于对象的引用(在C++中这一操作对应于ComCreateObject和QueryInterface)都会增加该对象的引用计数,释放对象引用则会减少引用计数(在C++中对应于Release)。在C++中对象和接口的创建和引用是显式的(需要编写代码),因此我们很清楚的知道哪些对象和接口应该在使用完成后释放掉,释放操作也是显式的。但在C#中,创建和引用是隐式的,释放却需要显式的,也即需要我们加入代码手工释放。在我们通过代码提示编写C#程序的时候,很可能并没有意识到刚才的语句其实正在创建或引用一个COM对象或接口,因此,也不会有显式的释放操作。这样一来,由于一直有未释放的引用,Excel就一直保持在内存中,直到。。。。。The End of the World或我们手工杀掉它。
每调用上一篇的代码来创建Excel Application一次,就会在任务管理器的列表中出现一个Excel.Exe,累积的结果,任谁都想得到。
可以通过以下方式释放对象引用:
- using System.Runtime.InteropServices;
- .......
- Marshal.ReleaseComObject(obj);
由于会多次用到该操作,需要写成一个方法:
- protected void releaseComObject(object obj)
- {
- try
- {
- System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
- }
- finally
- {
- }
- }
Excel的N多对象都会增加引用计数,因此,需要小心处理。一般来说,那些具有成员的对象和带s能够以数组访问的对象基本上都会产生引用,如Excel.Range.Font对象和Sheets对象、Cells对象等,都需要显式的释放。但这样也会产生新的问题:过于频繁的引用对象和释放引用,导致性能下降,如果最后再一起释放,内存消耗又巨大。这种情况需要根据实际情况区别对待。
此外,由于每次都是创建Excel.Application,最后销毁的方式操作,这样会带来启动Excel的额外消耗,大概需要10秒(在我的机器上)。
也可以写一个Singlton,由它创建一个唯一的Excel.Application实例,并完成数据操作,这样会有性能和开销上优势。
这就留待有心人完成吧,偶懒得写。