当我们使用非托管资源(unmanaged resources)类型时,应当使用IDisposable接口的Dispose()方法来释放资源。在.Net环境中,对非托管资源的回收不是系统的责任,我们必须自己调用Dispose()方法来释放资源。确保非托管资源会释放的最好方法是使用using或者try/finally。
所有的非托管资源类型都实现了IDisposable接口。另外当我们没有明确的释放资源,比如我们忘记了,C#还会防护性的通过创建终结器(finalizer)来释放资源。如果我们希望应用程序运行的更快时,就应当尽快释放一些不必要的资源。幸运的是在C#中有新的关键字来完成这项任务。我们先考虑下面的代码:
public void ExcuteCommand(string connectString, string commandString)
{
SqlConnection myConnection = new SqlConnection(connectString);
SqlCommand myCommand = new SqlCommand(commandString, myConnection);
myConnection.Open();
myCommand.ExecuteNonQuery();
}
有两个对象没有被释放掉:SqlConnection和SqlCommand。它们都会保存在内存中直到终结器被调用为止。
通过下面的修改,我们可以释放它们:
public void ExcuteCommand(string connectString, string commandString)
{
SqlConnection myConnection = new SqlConnection(connectString);
SqlCommand myCommand = new SqlCommand(commandString, myConnection);
myConnection.Open();
myCommand.ExecuteNonQuery();
myCommand.Dispose();
myConnection.Dispose();
}
这样做是正确的,但前提是SqlCommand没有抛出异常。一旦出现异常,我们的Dispose()方法就不会运行了。using关键字可以帮助我们确保Dispose()会被运行。当我们使用using的时候,C#的编译器会将它转换成为类似与try/finally的形式:
public void ExcuteCommand(string connectString, string commandString)
{
using(SqlConnection myConnection = new SqlConnection(connectString))
{
using(SqlCommand myCommand = new SqlCommand(commandString, myConnection))
{
myConnection.Open();
myCommand.ExecuteNonQuery();
}
}
}
下例中的两段代码会生成非常相似的IL
using(SqlConnection myConnection = new SqlConnection(connectString))
{
myConnection.Open();
}
try
{
SqlConnection myConnection = new SqlConnection(connectString);
myConnection.Open();
}
finally
{
myConnection.Dispose();
}
当我们使用非托管资源时,使用using是确保资源合理释放的简单途径。如果我们对不支持IDisposable接口的类型使用using关键字,编译器会报错:
//错误
using(string msg = "this is a message")
{
Console.WriteLine(msg);
}
另外using只检验编译时类型是否支持IDisposable接口,它不能识别运行时的对象。下例中即便Factory.CreateResource()返回的类型支持IDisposable接口也是不能通过编译的:
//错误
using(object obj = Factory.CreateResource)
{
}
对于可能支持可能不支持IDisposable接口的对象,我们可以这样来处理:
object obj = Factory.CreateResource();
using(obj as IDisposable)
{
}
如果对象实现了IDisposable接口,就可以生成释放资源的代码。如果不支持,则生成using(null),虽然不做任何工作,但也是安全的。如果我们拿不准是否应该将对象放在using中,那么比较稳妥的做法是将它放进去。
当我们在程序中使用了非托管资源类型时,我们应当将其放入using的括号中。当有多个需要释放的资源,例如前面的例子中的connection和command,我们应当创建多个using,每一个包含一个对应的对象。这些using会被转化为不同的try/finally块,在效果上看就好像是下面这段代码:
public void ExcuteCommand(string connectString, string commandString)
{
SqlConnection myConnection = null;
SqlCommand myCommand = null;
try
{
myConnection = new SqlConnection(connectString);
try
{
myCommand = new SqlCommand(commandString, myConnection);
myConnection.Open();
myCommand.ExecuteNonQuery();
}
finally
{
if(myCommand != null)
{