"不要多次释放对象"的小随笔

【题外话】

之前大部分时间都在用Visual Studio 2008做开发,虽然也点开过代码分析,但是一看一大串内容,尤其是一大串针对命名的建议,就果断关闭了。这次实习使用的Visual Studio 2012,发现代码分析默认去掉了很多内容,显示的也都是比较重要并需要改进的地方,所以也都认真研究了一下。

 

【文章索引】

  1. 问题和解决方法
  2. 为什么这样去做
  3. 相关链接

 

【一、问题和解决方法】

应该有人会写如下的代码吧,为了释放资源,我们把打开的东西都关闭掉,貌似没有什么问题。

复制代码
 1 FileStream fs = null;
 2 StreamReader sr = null;
 3 
 4 try
 5 {
 6     fs = new FileStream(@"F:\test.txt", FileMode.Open, FileAccess.Read);
 7     sr = new StreamReader(fs);
 8 
 9     String content = sr.ReadToEnd();
10 }
11 finally
12 {
13     if (sr != null)
14     {
15         sr.Close();
16     }
17 
18     if (fs != null)
19     {
20         fs.Close();
21     }
22 }
复制代码

当然,喜欢用using的同学也可能会写如下的代码:

1 using (FileStream fs = new FileStream(@"F:\test.txt", FileMode.Open, FileAccess.Read))
2 {
3     using (StreamReader sr = new StreamReader(fs))
4     {
5         String content = sr.ReadToEnd();
6     }
7 }

但是这两种代码如果使用代码分析会出现什么情况呢,如下图。

比较有意思的是,这里提示的是“不应对一个对象多次调用 Dispose”,为什么会是“一个对象”呢?

通过翻阅MSDN中的CA2202(链接在文后),我们可以查到原因是这样的,“某个方法实现所包含的代码路径可能导致对同一对象多次调用 IDisposable.Dispose 或与 Dispose 等效的方法(例如,用于某些类型的 Close() 方法)”,MSDN中直接给出了解决方法就是不要关闭StreamReader,而是直接关闭FileStream。

 

【二、为什么这样去做】

MSDN给出的方法为什么要这样做呢?出于好奇心,首先拿上述的代码单步调试一下:

在执行完StreamReader的Close之后,StreamReader的BaseStream指向了null,同时fs也变为了不能读取,但fs不是null。

然后我们用Reflector找到StreamReader的实现(在mscorlib.dll文件中)如下:

复制代码
 1 public override void Close()
 2 {
 3     this.Dispose(true);
 4 }
 5 
 6 protected override void Dispose(bool disposing)
 7 {
 8     try
 9     {
10         if ((this.Closable && disposing) && (this.stream != null))
11         {
12             this.stream.Close();
13         }
14     }
15     finally
16     {
17         if (this.Closable && (this.stream != null))
18         {
19             this.stream = null;
20             this.encoding = null;
21             this.decoder = null;
22             this.byteBuffer = null;
23             this.charBuffer = null;
24             this.charPos = 0;
25             this.charLen = 0;
26             base.Dispose(disposing);
27         }
28     }
29 }
复制代码

StreamReader在执行Close时竟然执行了this.stream(BaseStream)的Close,然后将BaseStream再指向null,这就解决了之前为什么提示不要多次释放 一个 对象,其实是StreamReader的Close已经释放了一次而已。当然,不仅仅是StreamReader是这样子,StreamWriter、BinaryReader等等也都是这样子的。

可是,为什么MSDN的例子给的是关闭流而不是关闭读取器呢?

翻阅了网上也没有找到权威的资料,所以个人总结了几点如下仅供参考:

1、关闭StreamReader等其实已经关闭了其BaseStream,但容易使开发者误以为BaseStream没有关闭而继续使用导致抛出异常,所以关闭最基础的流会更好些。

2、StreamReader等本身并没有使用非托管的内容,所以也无需主动执行Close,让GC去做就好了。

 

【三、相关链接】

1、CA2202:不要多次释放对象:http://msdn.microsoft.com/zh-cn/library/ms182334(v=vs.110).aspx

posted @   大魔王mAysWINd  阅读(2416)  评论(10编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述
点击右上角即可分享
微信分享提示