zoukankan      html  css  js  c++  java
  • 《Effective C#》读书笔记——条目15:使用using和try/finally清理资源<.NET资源管理>

      在.NET环境中,非托管系统资源由开发人员来负责释放,且非托管系统资源必须显式的使用IDisposable接口的Dispose()来释放(详见:了解.NET内存管理机制)。所有封装或使用了非托管资源的类型都实现了IDisposable接口。同时这些类也在终结器中调用Dispose(),保证了开发人员在忘记资源释放的时候仍然能够正常的释放掉资源(对象资源会一直停留在内存中,直到终结器被调用),这会导致资源在内存中停留的时间更长,导致应用程序会占用更多的系统资源。

    阅读目录:

        1.使用using关键字

            1.1 安全销毁对象

        2.同时销毁多个可销毁对象

        3.释放可销毁对象的方式

        小节

        参考&进一步阅读

    1.使用 using 关键字

       在C#中为我们添加了一个现实释放非托管资源的关键字:using。using语句其实是一个C#语言的语法糖,当我们在using语句中分配可释放对象时,C#编译器将会自动在每个对象外生成一个try/finally块来包裹住分配的对象,保证资源的及时释放,即使抛出了异常也一样。如果要使用一个可销毁的对象,使用using语句能够以最简单的方式保证你的对象可以正常销毁。看下面使用了using语句的代码:

    1             SqlConnection myConnection = null;
    2 
    3             using (myConnection = new SqlConnection(connectionString))
    4             {
    5                 myConnection.Open();
    6             }

    这段代码和下面的代码生成的IL代码完全一致

    1             try
    2             {
    3                 myConnection = new SqlConnection(connectionString);
    4                 myConnection.Open();
    5             }
    6             finally
    7             {
    8                 myConnection.Dispose();
    9             }

     

    使用using语句的注意事项:

    如果using语句中分配的变量的类型没有实现IDisposable接口,编译器将会抛出异常。

    1.1 安全销毁对象

       对于一些可能实现或未实现IDisposable接口的对象,或者无法确定是否应该用using语句包裹某个对象时,由于其不确定性我们可以使用as操作符进行安全的销毁,如下:

    1             object obj = Factory.CreateResource();
    2             //如果不确定obj是否实现了IDisposable接口,下面是安全的销毁方式
    3             using (obj as IDisposable)
    4             {
    5                 Console.WriteLine(obj.ToString());
    6             }

    如果obj实现了IDisposable接口,那么using语句将生成清理代码;反之,using语句将变成using(null) ,这并不会抛出异常,也不会有任何其他意料之外的操作。

    2.同时销毁多个可销毁对象

       通过前面我们知道使用using语句时编译器会将其转换为try/finally语句,这能够保证我们可以正确的将非托管对象销毁。但是当我们想要同时销毁多个对象时情况会有一点细微的变化,看下面的代码:

     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         }

     我们看到方法内部有两个非托管对象需要被释放,上面的代码运行的效果和我们的预期并没有差别,我们通过查看IL代码发现,这个示例生成的IL和下面的示例是一样的:

    View Code
     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, myConnection);
    11 
    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         }

     上面的代码并没有上面问题,运行效果可以达到了我们的预期,不过我们可以做得好点,直接使用try/finally语句,避免在这样情况中使用using语句而生成了多余的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 
    10                 myConnection.Open();
    11                 mySqlCommand.ExecuteNonQuery();
    12             }
    13             finally
    14             {
    15                 if (mySqlCommand != null)
    16                     mySqlCommand.Dispose();
    17                 if (myConnection != null)
    18                     myConnection.Dispose();
    19             }

     注意:

    必须保证每个实现了IDisposable接口的对象都放在了using或try/finally中,否则就可能会发生资源泄露。

    3.释放可销毁对象的方式

       我们发现在我们释放可销毁对象时。有的类型不但提供Dispose()方法还提供了一个Close方法,比如前面示例中SqlConnection类的myConnection对象,我们可以知道调用myConnection对象的Close方法关闭数据库连接,但是这和调用它的Dispose()有一些差别:Dispose()方法将调用GC.SuppressFinalize()方法,而Close()方法一般则不会,因此,即使已经不需要终结,但对象仍旧在终结队列中。如果两种方式你可以选择,应该优先使用Dispose方法。

      同时我们需要知道:Dispose()并不是将对象从内存中移除,而只是让对象释放掉其中的非托管资源

    小节

       .Net Framework 中只有不到100个类实现了IDisposable接口,当我们使用实现了IDisposable接口的类时,我们要保证能够正确的进行清理工作,可以将这些对使用using语句或者是try/finally语句包裹起来。无论使用哪种方式,都要保证对象在任何时候、任何情况下都被正确的销毁。

    参考资源&进一步阅读

    终结器

    using 语句

    实现 Finalize 和 Dispose 以清理非托管资源

  • 相关阅读:
    jquery中的ajax方法参数的用法和他的含义:
    链接
    数据库视图作用?什么时候用视图?
    八大排序算法的 Python 实现
    @wrap装饰器
    model方法取值总结
    js获取select改变事件
    SQL Server查询时添加一列连续的自增列
    UIAppearance使用详解-备
    NSString、NSData、char* 类型之间的转换-备
  • 原文地址:https://www.cnblogs.com/IPrograming/p/EffectiveCSharp_15.html
Copyright © 2011-2022 走看看