1. 程式人生 > >CSharp的介面顯式實現和IDisposable介面與using關鍵字的關係

CSharp的介面顯式實現和IDisposable介面與using關鍵字的關係

近日,有同事寫的http下載模組出現了一些問題,在Review程式碼的過程中發現一個奇怪的地方:

針對從WebResponse中取出來的Stream,在用完以後,對於Stream手動依次呼叫了Close、Dispose。

if (reader != null)
{
    reader.Close();
    reader.Dispose();
}

而緊接著,對於WebResponse的操作卻僅僅呼叫了Close。

if (response != null)
{
    response.Close();
}

而實際上,針對WebResponse的使用範例,無一例外都是使用了using關鍵字來處理Dispose的。

using (WebResponse response = request.GetResponse())
{
    ...
    response.Close();
}

這表明,WebResponse在使用完後,需要進行Dispose,於是我嘗試給他加上Dispose呼叫,卻發現通過response的引用,無法點出Dispose來,詢問原作者後,也表示因為點不出來,所以認為沒這個介面,就沒有呼叫了。但是,檢視WebResponse的定義,發現它是有繼承IDisposable的。

namespace System.Net
{
    public abstract class WebResponse : MarshalByRefObject, IDisposable, ISerializable
    {
        ...
    }
}

那麼,為什麼會點不出Dispose方法來呢?

原來,WebResponse對IDisposable介面的實現採用了顯式實現。

void IDisposable.Dispose()
{
    ...
}

對於顯式實現的介面,無法在子類的引用中呼叫該實現,而必須使用基類的引用。

if (response != null)
{
    response.Close();
    (response as IDisposable).Dispose();
}

這樣,可以成功呼叫Dispose,對其進行釋放。

那麼,為什麼使用using關鍵字時可以成功釋放呢?那是因為using在CSharp中的潛規則是,將賦予它的引用,強轉為IDisposable,然後呼叫它的Dispose介面。

using (object)
{
    ...
}
上述程式碼相當於
...
(object as IDisposable).Dispose();

特別的,如果傳入的object並沒有繼承自IDisposable,編譯時會直接報錯。

對於踩了這樣一個坑,說明這位同事的基礎知識掌握還不夠,並且在技術選型時有些盲目自信,在沒有完全掌握基礎細節的情況下,擅自改變了範例程式碼的常見用法,導致了這個結果。

在此,呼籲各位基礎還不紮實的同學,在學習範例程式碼的時候要麼徹底搞明白這麼寫的原因,要麼就嚴格模仿,不要擅自去修改,以免採坑,好自為之。