"不要多次釋放對象"的小隨筆


【題外話】

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM