C#如何释放非托管资源
.NET 平台在内存管理方面提供了GC(Garbage Collection),负责自动释放托管资源和内存回收的工作,但它无法对非托管资源进行释放,这时我们必须自己提供方法来释放对象内分配的非托管资源,比如你在对象的实现代码中使用了一个COM对象。Microsoft.Office.Interop.Excel就属于一个COM对象,因此由它生成的所有资源都是非团管资源。
根据MSDN上的描述:为适当释放非托管资源,建议您实现公共的 Dispose 或 Close 方法,这两个方法可为对象执行必要的清理代码操作。 IDisposable 接口为实现接口的资源类提供 Dispose 方法。 因为 Dispose 方法是公共的,所以应用程序用户可以直接调用该方法来释放非托管资源占用的内存。使用 Dispose 方法主要在使用本机资源的托管对象和向 .NET framework 公开 COM 对象。
链接:http://msdn.microsoft.com/zh-cn/library/498928w2.aspx
using语句
using的功能有:引入命名空间,为命名空间或类型创建别名。
using 语句还的一个作用是允许程序员指定使用资源的对象应当何时释放资源。为 using 语句提供的对象必须实现 IDisposable 接口。此接口提供了 Dispose 方法,该方法将释放此对象的资源。
使用规则:
a) using语句只能用于实现了IDisposable接口的类型,禁止为不支持IDisposable接口类型使用using语句,否则会出现编译错误
b) using语句适用于清理单个非托管资源的情况,而多个非托管对象的清理最好以try-finaly来实现,因为嵌套using语句可能存在隐藏的Bug.内层using块引发异常时,将不能释放外层using块的对象资源。
using实质:
在程序编译阶段,编译器会自动将using语句生成try-finally语句,并在finally块中调用对象的Dispose方法,来清理资源.所以,using语句等效于try-finally语句。
封装Excel操作类
综上我们可以封装一个Excel操作类,继承IDispose接口,实现Dispose方法释放Excel生成的所有非托管资源,最后由GC回收,在声明该类对象的时候用using(ExcelHelper excel=new ExcelHelper ()){ }。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
namespace ClassLibrary1
{
public class ExcelHelper :IDisposable
{
#region 变量
private Application _application ;
private Workbook _workbook;
private Worksheet _worksheet;
private Range _range;
#endregion
/// <summary>
/// 构造函数
/// </summary>
public ExcelHelper()
{
this._application = new Application();
this._workbook = this._application.Workbooks.Add(Type.Missing);
this._worksheet = (Worksheet)_workbook.Sheets.get_Item(1);
}
/// <summary>
/// 创建Excel返回路径?
/// </summary>
/// <returns></returns>
public string CreateExcel(bool isSleep=true)
{
_worksheet.Cells[1, 5] = "项目质量计划";
_range = (Range)_worksheet.get_Range("B1","L1");
_range.Merge(0);
_range = (Range)_worksheet.Cells[1, 5];
_range.EntireColumn.AutoFit();
//throw new Exception();
string filePath = AppDomain.CurrentDomain.BaseDirectory.ToString() + "QualityPlan\\Files";
if (!System.IO.Directory.Exists(filePath))
{
System.IO.Directory.CreateDirectory(filePath);
}
string FullName = "质量计划导出模板" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + ".xls";
string filefullpath = filePath + "\\" + FullName;
_application.ActiveWorkbook.SaveAs(filefullpath, Microsoft.Office.Interop.Excel.XlFileFormat.xlExcel12, null, null, false, false, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlNoChange, null, null, null, null);
return filefullpath;
}
/// <summary>
/// 释放资源,IDispose接口
/// </summary>
public void Dispose()
{
if (_workbook != null)
_workbook.Close(null, null, null);
if (_application != null)
{
_application.Workbooks.Close();
_application.Quit();
}
if (_range != null)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(_range);
_range = null;
}
if (_worksheet != null)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(_worksheet);
_worksheet = null;
}
if (_workbook != null)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(_workbook);
_workbook = null;
}
if (_application != null)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(_application);
_application = null;
}
GC.Collect();
}
}
}
using (ExcelHelper excelHelper = new ExcelHelper())
{
string filefullpath = excelHelper.CreateExcel();
}
以上方法在本机测试通过,但是用Microsoft.Office.Interop.Excel遇到大并发操作可能会出现等待问题,经测试当用户A占着Excel进程生成Excel文件时,用户B也点击导出Excel,此时就会出现B等待的状态,直到A 释放Excel资源。