Effective C#之15:Utilize using and Try/finally for Resource Cleanup

rel="File-List" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_filelist.xml"> rel="themeData" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_themedata.thmx"> rel="colorSchemeMapping" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_colorschememapping.xml">

Item 15: Utilize using and Try/finally for Resource Cleanup

利用usingTry/finally来进行资源清理

Types that use unmanaged system resources should be explicitly released using the Dispose() method of the IDisposable interface. The rules of the .NET environment make that the responsibility of the code that uses the type, not the responsibility of the type or the system. Therefore, anytime you use types that have a Dispose() method, it's your responsibility to release those resources by calling Dispose(). The best way to ensure that Dispose() always gets called is to utilize the using statement or a try/finally block.

使用未托管系统资源的类,应该通过使用IDisposable接口上的Dispose()方法来显式的释放。.Net环境的规则使这成为使用该类的代码的职责,而不是该类自己的职责或者系统的职责。因此,任何时候,你使用拥有Dispose()方法的类型时,通过调用Dispose()来释放资源,就成了你的职责。保证Dispose()总能被调用的最好的方法,是利用using或者try/catch

All types that own unmanaged resources implement the IDisposable interface. In addition, they defensively create a finalizer for those times when you forget to dispose properly. If you forget to dispose of those items, those nonmemory resources are freed later, when finalizers get their chance to execute. All those objects then stay in memory that much longer, and your application becomes a slowly executing resource hog.

所有拥有未托管资源的类型都实现了IDisposable接口。另外,当你忘记恰当的进行释放时,它们防卫性的创建终结器。如果你忘记释放这些东西,当终结器有机会执行的时候,这些非内存资源会晚点儿被释放。所有这些对象会在内存里驻留较长时间,你的应用程序就会变缓慢的吞噬可执行资源。

Luckily for you, the C# language designers knew that explicitly releasing resources would be a common task. They added keywords to the language that make it easy.

幸运的是,C#语言设计者早已知道,显式的释放资源将会是一个共性的任务。他们向语言里面加入了关键字使得这变得容易。

Suppose you wrote this code:

假设你写过这样的代码:

  1.     public void ExecuteCommand( String connString,String commandString )
  2.     {
  3.         SqlConnection myConnection = new SqlConnection( connString );
  4.         SqlCommand mySqlCommand = new SqlCommand( commandString,myConnection );
  5.         myConnection.Open();
  6.         mySqlCommand.ExecuteNonQuery();
  7.    }

Two disposable objects are not properly cleaned up in this example: SqlConnection and SqlCommand. Both of these objects remain in memory until their finalizers are called. (Both of these classes inherit their finalizer from System.ComponentModel.Component.)

在这个例子里面,2个可以被释放的对象SqlConnectionSqlCommand,没有被恰当的清理,都驻留在内存里,直到它们的终结器被调用。(所有这些类都从System.ComponentModel.Component继承了终结器)

You fix this problem by calling Dispose when you are finished with the command and the connection:

当完成了命令执行和连接时,可以调用Dispose来修复这个问题:

 

  1.    public void ExecuteCommand( String connString,String commandString )
  2.     {
  3.         SqlConnection myConnection = new SqlConnection( connString );
  4.         SqlCommand mySqlCommand = new SqlCommand( commandString,myConnection );
  5.          myConnection.Open();
  6.         mySqlCommand.ExecuteNonQuery();
  7.         mySqlCommand.Dispose( );
  8.         myConnection.Dispose( );
  9.     }

That's fine, unless any exceptions get thrown while the SQL command executes. In that case, your calls to Dispose() never happen. The using statement ensures that Dispose() is called. You allocate an object inside a using statement, and the C# compiler generates a try/finally block around each object:

除非在SQL命令被执行的过程中发生了异常,这都没有问题。那样的话,你对Dispose()的调用从不会发生。Using语句保证了Dispose()被调用。在using语句内部为进行对象分配,C#编译器会在每个对象外围生成try/catch代码块:

  1.     public void ExecuteCommand( string connString,string commandString )
  2.     {
  3.       using ( SqlConnection myConnection = new SqlConnection( connString ))
  4.       {
  5.         using ( SqlCommand mySqlCommand = new SqlCommand( commandString,myConnection ))
  6.         {
  7.           myConnection.Open();
  8.           mySqlCommand.ExecuteNonQuery();
  9.         }
  10.       }
  11.     }

Whenever you use one Disposable object in a function, the using clause is the simplest method to use to ensure that objects get disposed of properly. The using statement generates a Try/finally block around the object being allocated. These two blocks generate exactly the same IL:

无论何时,在一个方法内部使用支持Disposable对象的时候,要保证对象被正确的释放的话,using子句都是最简单的方式。Using表达式在正在被分配的对象周围生成一个try/catch块。这2块代码生成完全一样的IL

 

  1.    SqlConnection myConnection = null;
  2.     // Example Using clause:
  3.     using (myConnection = new SqlConnection(connString))
  4.     {
  5.         myConnection.Open();
  6.     }
  7.      // example Try/Catch block:
  8.     try
  9.     {
  10.         myConnection = new SqlConnection(connString);
  11.         myConnection.Open();
  12.     }
  13.     finally
  14.     {
  15.         myConnection.Dispose();
  16.     }

 

If you use the using statement with a variable of a type that does not support the IDisposable interface, the C# compiler generates an error. For example:

如果你对一个不支持IDisposable接口的类型变量使用using语句,C#会生成一个错误。例如:

  1.     // Does not compile:
  2.     // String is sealed, and does not support IDisposable.
  3.     using (string msg = "This is a message")
  4.         Console.WriteLine(msg);

The using statement works only if the compile-time type supports the IDisposable interface. You cannot use it with arbitrary objects:

只有当编译时类型支持IDisposable接口时,using语句才能工作。你不能让它和任意对象一起工作:

  1.     // Does not compile.
  2.     // Object does not support IDisposable.
  3.     using (object obj = Factory.CreateResource())
  4.         Console.WriteLine(obj.ToString());

A quick defensive as clause is all you need to safely dispose of objects that might or might not implement IDisposable:

一个快速防御性的as子句,是要安全释放可能实现或者没实现IDisposable对象时,所有需要的东西。

  1.     // The correct fix.
  2.     // Object may or may not support IDisposable.
  3.     object obj = Factory.CreateResource();
  4.     using (obj as IDisposable)
  5.         Console.WriteLine(obj.ToString());

If obj implements IDisposable, the using statement generates the cleanup code. If not, the using statement degenerates to using(null), which is safe but doesn't do anything. If you're not sure whether you should wrap an object in a using block, err on the side of safety: Assume that it does and wrap it in the using clause shown earlier.

如果obj实现了IDisposableusing语句就生成清理代码。如果没有,using语句就退化成using(null),这是安全的并且什么都不做。如果你不确定是否应该在using块里面包装一个对象,那就选择安全的方式:假设它能,使用前面展示的using子句包装它。

That covers the simple case: Whenever you use one disposable object that is local to a method, wrap that one object in a using statement. Now you can look at a few more complicated usages. Two different objects need to be disposed in that first example: the connection and the command. The example I showed you creates two different using statements, one wrapping each of the two objects that need to be disposed. Each using statement generates a different try/finally block. In effect, you have written this construct:

已经覆盖了简单的情况:无论何时,当你使用一个支持Disposable的方法内部的局部对象时,都使用using语句包裹它。现在,来看一些更加复杂的应用。在第一个例子里面,2个不同的对象需要被释放:connectioncommand。我展示给你的例子创建了2个不同的using语句,每个包裹一个需要被Disposable的对象。每个using语句生成一个不同的try/catch块。从效果上来讲,你写下了这样的结构:

 

  1.    public void ExecuteCommand(string connString, string commandString)
  2.     {
  3.         SqlConnection myConnection = null;
  4.         SqlCommand mySqlCommand = null;
  5.         try
  6.         {
  7.             myConnection = new SqlConnection(connString);
  8.             try
  9.             {
  10.                 mySqlCommand = new SqlCommand(commandString,
  11.                 myConnection);
  12.                 myConnection.Open();
  13.                 mySqlCommand.ExecuteNonQuery();
  14.             }
  15.             finally
  16.             {
  17.                 if (mySqlCommand != null)
  18.                     mySqlCommand.Dispose();
  19.             }
  20.         }
  21.         finally
  22.         {
  23.             if (myConnection != null)
  24.                 myConnection.Dispose();
  25.         }
  26.     }

Every using statement creates a new nested Try/finally block. I find that an ugly construct, so when I allocate multiple objects that implement IDisposable, I prefer to write my own try/finally blocks:

每个using语句创建一个内嵌的Try/finally块。我发现这是丑陋的结构,因此当我为多个实现IDisposable的对象进行分配时,我更愿意写下我自己的try/finally块:

 

  1.    public void ExecuteCommand(string connString,string commandString)
  2.     {
  3.         SqlConnection myConnection = null;
  4.         SqlCommand mySqlCommand = null;
  5.         try
  6.         {
  7.             myConnection = new SqlConnection(connString);
  8.             mySqlCommand = new SqlCommand(commandString,myConnection);
  9.             myConnection.Open();
  10.             mySqlCommand.ExecuteNonQuery();
  11.         }
  12.         finally
  13.         {
  14.             if (mySqlCommand != null)
  15.                 mySqlCommand.Dispose();
  16.             if (myConnection != null)
  17.                 myConnection.Dispose();
  18.         }
  19.     }

However, don't get too cute and try to build one using clause with as statements:

然而,不要太搞笑,试图创建一个和as表达式在一起的using子句

  1.     public void ExecuteCommand(string connString,string commandString)
  2.     {
  3.         // Bad idea. Potential resource leak lurks!
  4.         SqlConnection myConnection = new SqlConnection(connString);
  5.         SqlCommand mySqlCommand = new SqlCommand(commandString, myConnection);
  6.         using (myConnection as IDisposable)
  7.         using (mySqlCommand as IDisposable)
  8.         {
  9.             myConnection.Open();
  10.             mySqlCommand.ExecuteNonQuery();
  11.         }
  12.     }

It looks cleaner, but it has a subtle bug. The SqlConnection object never gets disposed if the SqlCommand() constructor throws an exception. You must make sure that any objects that implement IDisposable are allocated inside the scope of a using block or a try block. Otherwise, resource leaks can occur.

看起来很清洁,但是它有微小的bug。如果SqlCommand构造函数抛出异常的话,SqlConnection对象从来不会被释放。你必须保证,任何实现了IDisposable接口的对象都在using内部或者try内部进行分配。否则,资源泄漏的情况就会发生。

So far, you've handled the two most obvious cases. Whenever you allocate one disposable object in a method, the using statement is the best way to ensure that the resources you've allocated are freed in all cases. When you allocate multiple objects in the same method, create multiple using blocks or write your own single try/finally block.

到目前为止,你已经掌握了2个最明显的情况。无论何时,你在一个方法内部分配一个可以disposable的对象,using语句总是保证在任何情况下,已经分配的资源会被释放的最好的方式。当你在一个方法内部为多个对象进行分配时,创建多个using块,或者编写自己单独的try/finally块。

There is one more nuance to freeing disposable objects. Some types support both a Dispose method and a Close method to free resources. SqlConnection is one of those classes. You could close SqlConnection like this:

在释放disposable对象时,还有个细微的差别。一些类型同时支持Dispose方法和Close方法,都可以来释放资源。SqlConnection就是这样的一个。可以像这样子来关闭SqlConnection

 

  1.    public void ExecuteCommand(string connString,string commandString)
  2.     {
  3.         SqlConnection myConnection = null;
  4.         try
  5.         {
  6.             myConnection = new SqlConnection(connString);
  7.             SqlCommand mySqlCommand = new SqlCommand(commandString,myConnection);
  8.             myConnection.Open();
  9.             mySqlCommand.ExecuteNonQuery();
  10.         }
  11.         finally
  12.         {
  13.             if (myConnection != null)
  14.                 myConnection.Close();
  15.         }
  16.     }

This version does close the connection, but that's not exactly the same as disposing of it. The Dispose method does more than free resources: It also notifies the Garbage Collector that the object no longer needs to be finalized. Dispose calls GC.SuppressFinalize(). Close typically does not. As a result, the object remains in the finalization queue, even though finalization is not needed. When you have the choice, Dispose() is better than Close(). You'll learn all the gory details in Item 18.

该版本确实关闭了连接,但是和disposing做得并不完全一样。Dispose方法做得不只是释放资源,它也通知GC:这个对象不再需要终结了。Dispose调用GC.SuppressFinalize()Close一般不会,因此,对象仍然在终结队列中,即使并不需要终结过程。当你可以选择时,Dispose()Close()要好。你会在Item 18里面看到值得夸耀的细节。

Dispose() does not remove objects from memory. It is a hook to let objects release unmanaged resources. That means you can get into trouble by disposing of objects that are still in use. Do not dispose of objects that are still being referenced elsewhere in your program.

Dispose()不从内存里面移除对象。它是一个钩子,让对象来释放未托管的资源。那意味着,如果释放还在使用的对象,会陷入麻烦中。不要释放在你程序任何地方正在被引用的对象。

In some ways, resource management can be more difficult in C# than it was in C++. You can't rely on deterministic finalization to clean up every resource you use. But a garbage-collected environment really is much simpler for you. The vast majority of the types you make use of do not implement IDisposable. Less than 100 classes in the .NET Framework implement IDisposablet hat's out of more than 1,500 types. When you use the ones that do implement IDisposable, remember to dispose of them in all cases. You should wrap those objects in using clauses or Try/finally blocks. Whichever you use, make sure that objects get disposed properly all the time, every time.

在某些方面,资源管理在C#里面比在C++里面更困难。你不能依靠不确定的终结器来清理你使用的每个资源。但是可以收集垃圾的环境确实比较简单。你利用的绝大半类型没有实现IDisposable。在.Net框架里,有多于1500个类型,但是只有不足100个类实现了IDisposable。当你使用没有实现IDisposable的类型时,记得在所有的情况下释放它们。你应该将这些对象包裹在using或者try/finally里面。不管你用那个,每次总要确保这些对象被恰当的释放。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值