C#資源回收和IDisposable接口的使用

源碼地址:https://github.com/hiramtan/HiFramework_unity/blob/master/unity/Assets/HiFramework/Extensions/ObjectBase.csgit

在說資源回收以前先要說明託管資源和非託管資源。github

1.託管資源由CLR來維護,自動進行垃圾回收,好比數組。數組

2.非託管資源不會進行自動垃圾回收,須要手動釋放,好比句柄。但在C#中的非託管資源不少都被封裝到.NET類中,當對象釋放時內部方法同時釋放非託管資源。緩存

好比Socket鏈接,在.Net中被封裝爲Socket類,反編譯Socket類庫,看到建立鏈接對象時其實建立了一個句柄安全

可是當上層用戶使用socket的時候並無發現有過釋放句柄的邏輯,這部分釋放邏輯由誰完成的?咱們繼續反編譯Socket的Close方法。socket

最終將會調用到本篇所要涉及的IDisposable接口。在Disposable中Socket內部邏輯釋放了託管資源好比緩存和非託管資源,好比鏈接句柄。函數

既然說清楚了託管資源和非託管資源,下面正式說下IDisposable接口的使用。this

在C#中建立的對象,數組,列表...等等都不須要考慮資源釋放的問題,由於它會被CLR的垃圾回收機制自動回收。好比有個ObjectBase類,建立這個對象而後置空,它會一段時間後被自動回收:對象

ObjectBase ob = new ObjectBase();blog

ob = null;

那怎樣肯定這個對象確實是被正常回收了呢,咱們引入它的析構函數:

public class ObjectBase

{

~ObjectBase()

{

Console.WriteLine("Dispose finish");

}

}

如今出現一個問題:建立的對象釋放時是隨機的不肯定時長的等待自動回收機制進行收回,有沒有辦法主動回收這些資源呢,好比對象佔用內存較大,想主動當即釋放而不等待主動回收。能夠寫一個方法,在這個方法裏釋放引用的資源就能夠了,這個方法能夠隨便起名,固然更規範的是繼承IDisposable來實現Disposable方法。

好比在這個方法裏釋放申請的託管資源和非託管資源等,如今ObjectBase類變成以下:

public class ObjectBase:IDisposable

{

~ObjectBase()

{

Console.WriteLine("Dispose finish");

}

 

/// <summary>執行與釋放或重置非託管資源關聯的應用程序定義的任務。</summary>

public void Dispose()

{

}

}

如今又出現一個問題,有些用戶會調用ob.Dispose()同時釋放了託管資源和非託管資源,有些用戶只是ob = null什麼都沒有作等待系統的垃圾回收。能不能有更統一的方法?固然能夠,咱們把釋放資源的邏輯不寫在Dispose()方法中,而是寫在另外一個方法中,好比Dispose(bool disposing)中,讓用戶主動調用的,或者析構函數都調用這個方法就同時作到了釋放資源。這樣還有一個問題,當用戶主動調用Dispose()時,其實Dispose(bool disposing)被執行了兩次(主動調用一次,析構一次)咱們添加一個標識,標記他只能被標記一次防止屢次調用。

public class ObjectBase : IDisposable

{

private bool disposed = false;

~ObjectBase()

{

Dispose(false);

}

/// <summary>執行與釋放或重置非託管資源關聯的應用程序定義的任務。</summary>

public void Dispose()

{

Dispose(true);

}

private void Dispose(bool disposing)

{

if (!disposed)

{

 

}

disposed = true;

}

}

再繼續,Dispose(bool disposing)爲何傳入bool類型的變量?其實這個變量標記的是是否可以安全釋放,託管資源由垃圾回收機制回收,當析構函數執行時它所引用的對象說不定早就被回收,好比再去取這個List,而後執行Clear時確定會報錯,由於List早已被回收。當用戶調用Dispose接口時,這個對象還未觸發垃圾回收,能夠隨意拿來Clear,因此邏輯又會變成:

public class ObjectBase : IDisposable

{

private bool disposed = false;

~ObjectBase()

{

Dispose(false);

}

/// <summary>執行與釋放或重置非託管資源關聯的應用程序定義的任務。</summary>

public void Dispose()

{

Dispose(true);

}

private void Dispose(bool disposing)

{

if (!disposed)

{

if (disposing)

{

//釋放託管資源

}

//釋放非託管資源

}

disposed = true;

}

}

這樣也會存在一個問題:當用戶主動調用Dispose時釋放非託管的邏輯執行了兩次(一次主動調用,一次析構)那咱們來通知系統不要執行回收該對象來避免兩次執行,至此釋放接口的邏輯完成:

public class ObjectBase : IDisposable

{

private bool disposed = false;

~ObjectBase()

{

Dispose(false);

}

/// <summary>執行與釋放或重置非託管資源關聯的應用程序定義的任務。</summary>

public void Dispose()

{

Dispose(true);

GC.SuppressFinalize(this);

}

private void Dispose(bool disposing)

{

if (!disposed)

{

if (disposing)

{

//釋放託管資源

}

//釋放非託管資源

}

disposed = true;

}

}

可是用戶使用起來太不方便了,咱們封裝一下,能夠繼承該類方便的使用。由於釋放託管資源在C#裏面操做很頻繁,非託管資源大部分被封裝只是不多的狀況下使用,咱們分別把接口封裝爲強制重寫和可重寫。

public abstract class ObjectBase : IDisposable

{

private bool disposed = false;

~ObjectBase()

{

Dispose(false);

}

/// <summary>執行與釋放或重置非託管資源關聯的應用程序定義的任務。</summary>

public void Dispose()

{

Dispose(true);

GC.SuppressFinalize(this);

}

private void Dispose(bool disposing)

{

if (!disposed)

{

if (disposing)

{

DisposeManaged();

}

DisposeUnmanaged();

}

disposed = true;

}

 

protected abstract void DisposeManaged();//釋放託管資源

 

protected virtual void DisposeUnmanaged()//釋放非託管資源

{

 

}

}

相關文章
相關標籤/搜索