c#对于如何释放资源的解释

时间:2020-12-27 03:39:29
 

当我们使用非托管资源(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)
                    {