C#資源釋放及Dispose、Close和析構方法

C#資源釋放及Dispose、Close和析構方法

 

備註:此文的部分觀點有誤,之所以仍舊保留本文,是需要在後期給出一個勘誤版。正確的版本在這裏“C#中標準Dispose模式的實現

 

一:什麼是資源

在開始本文前,需要一些準備知識。首先要提出“什麼是資源”。在CLR出來之後,Windows系統資源開始分爲“非託管資源”和“託管資源”。

         非託管資源是指:所有的Window內核對象(句柄)都是非託管資源,如對於Stream,數據庫連接,GDI+的相關對象,還有Com對象等等,這些資源並不是受到CLR管理;

         託管資源是指:由CLR管理分配和釋放的資源,即由CLR裏new出來的對象。

其次再來講,資源的釋放方式。

         非託管資源:需要顯式釋放的,也即需要你寫代碼釋放;

         託管資源:並不需要顯式釋放,但是如果引用類型本身含有非託管資源,則需要進行現實釋放;

二:顯式釋放的C#實現

顯式釋放的C#實現,由C#語法支持的有:

         1:實現IDisposable接口的Dispose方法;

         2:析構方法(終結器);

         不由C#語法支持,但是約定支持的顯式釋放是:

         3:提供顯示釋放方法,比如常用的Close方法;

三:Dispose、Close和析構方法異同點

但是,還需要區分這3種方式的異同點。首先,你無法調用析構方法。析構方法是由垃圾回收機制進行調用的。換句話來說,就是你不知道析構方法被調用的時機。嚴格意義上來說,它只是作爲資源釋放的一個補救措施。

資源釋放的一個正確的措施是爲類型實現IDisposable接口的Dispose。當你需要釋放類型的資源的時候,應該顯示的調用Dipose方法。當然,這裏還有一個C#的語法糖,就是使用using程序塊,在離開using程序塊的時候,CLR會自動調用類型所創建對象的Dipose方法。

可能有人會問道,既然可以通過Dispose方法的方式來進行資源的釋放,爲什麼有些類型還需要提供一個Close方法。這裏面的區別,或者說約定在於,如果你仔細觀察這些類型:他們基本都只公開了Close方法,他們都實現了IDisposable,但都隱藏了Dispose方法。以Socket這個類爲例,它:

1:提供public void Close()

public void Close()
{
    //….
    ((IDisposable)this).Dispose();
    //…. 
}

2:提供顯式void IDisposable.Dispose()

void IDisposable.Dispose() 
{ 
        this.Dispose(true);
        GC.SuppressFinalize(this);
}

3:提供protected virtual void Dispose(bool disposing)。真正的資源釋放的代碼放在這裏。

所以理論上來將,提供Close方法最終還是使用的Dispose方法,之所以這麼做,是因爲這些類型出於顯式實現IDisposable的因素,在調用這些Dispose方法的時候,必須完成一次轉型,如: 

           ((IDisposable)new A()).Dispose(); 

爲了避免轉型,同時也爲了避免不熟悉C#語法的開發人員更直觀的釋放資源,提供了Close方法。

在上文的例子中,你可能已經注意到IDisposable.Dispose這個方法中,包含一句: 

      GC.SuppressFinalize(this); 

這是告訴CLR,在進行垃圾回收的時候,不用再繼續調用析構方法(終結器)了。是的,因爲你已經手動釋放資源了。這也從另一個方面驗證了析構方法只是作爲資源釋放的補救機制。因爲假設你忘記Close或者Dispose了,CLR會在垃圾回收的時候爲你做這件事。查看Socket的析構函數,你會很好的理解這一點

~Socket()
{
	this.Dispose(false);
}

 

是的,析構方法調用的也是Dispose。

備註1:本文帶來幾個爭論

1:託管資源本身是否需要顯式釋放。答案顯然是:不需要;

2:如果引用類型對象不再需要,是否需要顯式=null;答案是:即使不這樣做,GC也會進行垃圾回收。

3:將託管資源分爲引用類型資源和值類型資源這種分類方法是有問題的,或者說是錯誤的。正確的分類法應該是棧資源和堆資源。線程棧中存放的是方法的實參和方法內部的局部變量。堆上存放的是類型對象本身及對象的兩個額外成員:類型對象指針和同步塊索引。

4:Dispose方法本身是用來讓你放置資源清理代碼的。顯然,一個空方法並不代表清理工作本身,真正執行清理工作的是你具體的代碼。

備註2:推薦Dipose模式實現

如:基類

class ClassShouldDisposeBase : IDisposable
{
    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            //執行基本的清理代碼
        }
    }

    ~ClassShouldDisposeBase()
    {
        this.Dispose(false);
    }

}

子類:

class ClassShouldDispose : ClassShouldDisposeBase
{
    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // 執行子類清理代碼
            // 如有必要,執行base.Dispose(disposing);
        }
        else
        {
            // 如有必要,執行base.Dispose(disposing);
        }
    }

    public void Close()
    {
        //調用本類或者基類的Dispose方法
        //其它代碼
    }
}

 原文連接:https://www.cnblogs.com/luminji/archive/2011/01/05/1926468.html

 

發佈了47 篇原創文章 · 獲贊 10 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章