《Effective C#》读书笔记——条目15:使用using和try/finally清理资源<.NET资源管理>

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介:

.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

 

 上面的代码并没有上面问题,运行效果可以达到了我们的预期,不过我们可以做得好点,直接使用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 以清理非托管资源

分类:  C#

.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

 

 上面的代码并没有上面问题,运行效果可以达到了我们的预期,不过我们可以做得好点,直接使用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 以清理非托管资源

分类:  C#
本文转自gyzhao博客园博客,原文链接:http://www.cnblogs.com/IPrograming/archive/2012/11/26/EffectiveCSharp_15.html ,如需转载请自行联系原作者
相关文章
|
XML 编解码 开发工具
《移动互联网技术》第六章 资源管理: 掌握定制控件样式、界面主题、可绘制资源程序的编写方法
《移动互联网技术》第六章 资源管理: 掌握定制控件样式、界面主题、可绘制资源程序的编写方法
59 0
|
2月前
|
传感器 人工智能 供应链
.NET开发技术在数字化时代的创新作用,从高效的开发环境、强大的性能表现、丰富的库和框架资源等方面揭示了其关键优势。
本文深入探讨了.NET开发技术在数字化时代的创新作用,从高效的开发环境、强大的性能表现、丰富的库和框架资源等方面揭示了其关键优势。通过企业级应用、Web应用及移动应用的创新案例,展示了.NET在各领域的广泛应用和巨大潜力。展望未来,.NET将与新兴技术深度融合,拓展跨平台开发,推动云原生应用发展,持续创新。
50 4
|
3月前
|
数据库连接 开发者
.NET 内存管理两种有效的资源释放方式
【10月更文挑战第15天】在.NET中,有两种有效的资源释放方式:一是使用`using`语句,适用于实现`IDisposable`接口的对象,如文件流、数据库连接等,能确保资源及时释放,避免泄漏;二是手动调用`Dispose`方法并处理异常,提供更灵活的资源管理方式,适用于复杂场景。这两种方式都能有效管理资源,提高应用性能和稳定性。
|
3月前
|
算法 Java 数据库连接
.NET 内存管理两种有效的资源释放方式
【10月更文挑战第14天】在 .NET 中,`IDisposable` 接口提供了一种标准机制来释放非托管资源,如文件句柄、数据库连接等。此类资源需手动释放以避免泄漏。实现 `IDisposable` 的类可通过 `Dispose` 方法释放资源。使用 `using` 语句可确保资源自动释放。此外,.NET 的垃圾回收器会自动回收托管对象所占内存,提高程序效率。示例代码展示了如何使用 `MyFileHandler` 类处理文件操作并释放 `FileStream` 资源。
|
4月前
|
资源调度 分布式计算 运维
Hadoop集群资源管理篇-资源调度器
详细介绍了Hadoop集群资源管理中的资源调度器,包括资源分配的概念、大数据运维工程师如何管理集群工作负载、资源调度器的背景、Hadoop提供的FIFO、容量调度器和公平调度器三种资源调度器的概述以及它们之间的对比。
161 4
|
5月前
|
开发框架 前端开发 .NET
究竟是什么让.NET 开发者社区拥有如此强大的力量?资源、分享还是成长的秘密?
【8月更文挑战第28天】.NET开发者社区为成员提供了丰富的资源、积极的分享氛围和广阔的成长空间,是一个充满活力的知识宝库。在这里,从前沿的开源项目到深入的技术解析应有尽有,无论你是新手还是专家,都能找到适合自己的学习与交流机会,共同推动.NET技术的发展。
47 5
|
5月前
|
资源调度 Kubernetes 调度
Flink 细粒度资源管理问题之细粒度资源请求满足问题如何解决
Flink 细粒度资源管理问题之细粒度资源请求满足问题如何解决
|
5月前
|
资源调度 调度 流计算
Flink 细粒度资源管理问题之为不同的SSG配置资源如何解决
Flink 细粒度资源管理问题之为不同的SSG配置资源如何解决
|
5月前
|
存储 NoSQL 分布式数据库
Flink 细粒度资源管理问题之调整 slot 配置来提高资源利用效率如何解决
Flink 细粒度资源管理问题之调整 slot 配置来提高资源利用效率如何解决
阿里云的服务器资源在那?点击工作台,之后点击资源管理
阿里云的服务器资源在那?点击工作台,之后点击资源管理