您好,登錄后才能下訂單哦!
(二)析構函數
析構函數
· 不能在結構中定義析構函數。只能對類使用析構函數。
· 一個類只能有一個析構函數。
· 無法繼承或重載析構函數。
· 無法調用析構函數。它們是被自動調用的。
· 析構函數既沒有修飾符,也沒有參數。
析構函數(destructor) 與構造函數相反,當對象脫離其作用域時(例如對象所在的函數已調用完畢),系統自動執行析構函數。析構函數往往用來做“清理善后” 的工作(例如在建立對象時用new開辟了一片內存空間,應在退出前在析構函數中用delete釋放)。
以C++語言為例,析構函數名也應與類名相同,只是在函數名前面加一個波浪符~,例如~stud( ),以區別于構造函數。它不能帶任何參數,也沒有返回值(包括void類型)。只能有一個析構函數,不能重載。如果用戶沒有編寫析構函數,編譯系統會自動生成一個缺省的析構函數,它也不進行任何操作。所以許多簡單的類中沒有用顯式的析構函數。
解構器
我們知道,‘解構器’被用來清除類的事例。當我們在C#中使用解構器是,我們必須記住以下幾點:
一個類只能有一個解構器。
解構器不能被繼承或重載。
解構器不能被調用。他們是自動被(編譯器)調用的。
解構器不能帶修飾或參數。
下面是類MyClass解構器的一個聲明:
~ Class()
{
// Cleaning up code goes here
}
程序員不能控制解構器何時將被執行因為這是由垃圾收集器決定的。垃圾收集器檢查不在被應用程序使用的對象。它認為這些條件是符合清楚的并且收回它們的內存。解構器也在程序退出時被調用。當解構器執行時其背后所發生的那一幕是解構器隱式調用對象基類的Object.Finalize方法。因此上述解構器代碼被隱含轉化成:
protected override void Finalize()
{
try
{
// Cleaning up .
}
finally
{
base.Finalize();
}
}
現在,讓我們看一個解構器怎樣被調用的例子。我們有三個類A,B和C 。B派生自A,C派生自B。每個類有它們自己的構造器和解構。在類App的main函數中,我們創建C的對象。
using System;
class A
{
public A()
{
Console.WriteLine("Creating A");
}
~A()
{
Console.WriteLine("Destroying A");
}
}
class B:A
{
public B()
{
Console.WriteLine("Creating B");
}
~B()
{
Console.WriteLine("Destroying B");
}
}
class C:B
{
public C()
{
Console.WriteLine("Creating C");
}
~C()
{
Console.WriteLine("Destroying C");
}
}
class App
{
public static void Main()
{
C c=new C();
Console.WriteLine("Object Created ");
Console.WriteLine("Press enter to Destroy it");
Console.ReadLine();
c=null;
//GC.Collect();
Console.Read();
}
}
正如我們預料的,基類的構造器將會被執行并且程序會等待用戶按‘enter’。當這個發生,我們把類C的對象置為null.但解構器沒有被執行..!!??正像我們所說的,程序員無法控制解構器何時被執行因為這是由垃圾搜集器決定的。但程序退出時解構器被調用了。你能通過重定向程序的o/p到文本文件來檢查這個。我將它輸出在這里。注意到基類的解構器被調用了,因為在背后base.Finalize()被調用了。
Creating A
Creating B
Creating C
Object Created
Press enter to Destroy it
Destroying C
Destroying B
Destroying A
所以,如果一旦你使用完對象你就想調用解構器,你該怎么做?有兩個方法:
調用垃圾搜集器來清理。
實現IDisposable的Dispose方法。
調用垃圾搜集器
你能通過調用GC.Collect方法強制垃圾搜集器來清理內存,但在大多數情況下,這應該避免因為它會導致性能問題。在上面的程序中,在GC.Collect()處移除注釋。編譯并運行它。現在,你能看到解構器在控制臺中被執行了。
實現IDisposable接口
IDisposable 接口包括僅有的一個公共方法,其聲明為void Dispose()。我們能實現這個方法來關閉或釋放非托管資源如實現了這個接口的類事例所控制的文件,流,和句柄等。這個方法被用做所有任務聯合對象的資源釋放。當實現了這個方法,對象必須尋求確保所有擁有的資源被繼承結構中關聯的資源也釋放(不能把握,翻不出來)。
class MyClass:IDisposable
{
public void Dispose()
{
//implementation
}
}
當我們實現了IDisposable接口時,我們需要規則來確保Dispose被適當地調用。
聯合使用解構器和IDisposable接口
Public class MyClass:IDisposable
{
private bool IsDisposed=false;
public void Dispose()
{
Dispose(true);
GC.SupressFinalize(this);
}
protected void Dispose(bool Diposing)
{
if(!IsDisposed)
{
if(Disposing)
{
//Clean Up managed resources
}
//Clean up unmanaged resources
}
IsDisposed=true;
}
~MyClass()
{
Dispose(false);
}
}
在這里重載了Dispose(bool)來做清理工作,并且所有的清理代碼都僅寫在這個方法中。這個方法被解構器和IDisposable.Dispose()兩著調用。我們應該注意Dispose(bool)沒有在任何地方被調用除了在IDisposable.Dispose()和解構器中。
當一個客戶調用IDisposable.Dispose()時,客戶特意地想要清理托管的和非托管資源,并且因此完成清理工作。有一件你必須注意的事情是我們在清理資源之后立即調用了GC.SupressFinalize(this)。這個方法通知垃圾搜集器不需要調用解構器,因為我們已經做了清理。
注意上面的例子,解構器使用參數false調用Dispose。這里,我們確信垃圾搜集器搜集了托管資源。我們僅僅做非托管資源的清理。
結論
盡管如此我們花費一些時間實現IDisposable接口,如果客戶不能合適地調用它們會怎樣?為此C#有一個酷的解決方案。‘using’代碼塊。它看起來像這樣:
using (MyClass objCls =new MyClass())
{
}
當控制從using塊通過成功運行到結束或者拋出異常退出時,MyClass的IDispose.Dispose()將會被執行。記住你例示的對象必須實現System.IDisposable接口。using語句定義了哪個對象將被清除的一個范圍。
注:
構造函數與析構函數的區別:
構造函數和析構函數是在類體中說明的兩種特殊的成員函數。
構造函數的功能是在創建對象時,使用給定的值來將對象初始化。
析構函數的功能是用來釋放一個對象的。在對象刪除前,用它來做一些清理工作,它與構造函數的功能正好相反。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。