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


    【题外话】

    之前大部分时间都在用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


    如果您觉得本文对您有所帮助,不妨点击下方的“推荐”按钮来支持我!

    本文及文章中代码均基于“署名-非商业性使用-相同方式共享 3.0”,文章欢迎转载,但请您务必注明文章的作者和出处链接,如有疑问请私信我联系!

  • 相关阅读:
    Java Collection知识总结
    Java异常总结
    关于触发器
    关于事务
    git分支的创建、删除、切换、合并
    github项目上传管理
    如何在github上下载单个文件夹?
    常见的javascript跨站
    各类常用端口漏洞缺陷
    SEO优化实践操作
  • 原文地址:https://www.cnblogs.com/mayswind/p/3013293.html
Copyright © 2020-2023  润新知