C#中結構函數和析構函數的用法


轉自站長資訊:http://news.55.la/Program/.Net/17612.html

 

摘 要:結構函數與析構函數是一個類中看似較為簡略的兩類函數,但在實際應用過程中總會呈現一些意想不到的運行錯誤。本文將較系統的先容結構函數與析構函數的原理及在C#中的應用,以及在應用過程中需要留心的若干事項。

要害字:結構函數;析構函數;垃圾回收器;非托管資源;托管資源
一.結構函數與析構函數的原理

作為比C更先進的語言,C#供給了更好的機制來加強程序的安全性。C#編譯用具有嚴格的類型安全檢查功效,它幾乎能找出程序中所有的語法標題,這的確幫了程序員的大忙。但是程序通過了編譯檢查並不表現錯誤已經不存在了,在“錯誤”的大家庭里,“語法錯誤”的地位只能算是冰山一角。級別高的錯誤通常暗躲得很深,不輕易發明。

根據經驗,不少難以察覺的程序錯誤是由於變量沒有被准確初始化或清除造成的,而初始化和清除工作很輕易被人遺忘。微軟利用面向對象的概念在設計C#語言時充分考慮了這個標題並很好地予以解決:把對象的初始化工作放在結構函數中,把清除工作放在析構函數中。當對象被創立時,結構函數被主動履行。當對象滅亡時,析構函數被主動履行。這樣就不用擔心忘記對象的初始化和清除工作。
二.結構函數在C#中的應用

結構函數的名字不能隨便起,必需讓編譯器認得出才可以被主動履行。它的命名方法既簡略又公平:讓結構函數與類同名。除了名字外,結構函數的另一個特別之處是沒有返回值類型,這與返回值類型為void的函數不同。假如它有返回值類型,那么編譯器將不知所措。在你可以拜訪一個類的方法、屬性或任何其它東西之前, 第一條履行的語句是包含有相應類的結構函數。甚至你自己不寫一個結構函數,也會有一個缺省結構函數供給應你。

class TestClass
{
public TestClass(): base() {} // 由CLR供給
}

下面列舉了幾種類型的結構函數

1)缺省結構函數

class TestClass
{
public TestClass(): base() {}
}

上面已先容,它由系統(CLR)供給。
2)實例結構函數

實例結構函數是實現對類中實例進行初始化的方法成員。如:

using System;
class Point
{
public double x, y;
public Point()
{
this.x = 0;
this.y = 0;
}

public Point(double x, double y)
{
this.x = x;
this.y = y;
}

}

class Test
{
static void Main()
{
Point a = new Point();
Point b = new Point(3, 4); // 用結構函數初始化對象

}
}

聲明了一個類Point,它供給了兩個結構函數。它們是重載的。一個是沒有參數的Point結構函數和一個是有兩個double參數的Point結構函數。假如類中沒有供給這些結構函數,那么會CLR會主動供給一個缺省結構函數的。但一旦類中供給了自定義的結構函數,如Point()和Point(double x, double y),則缺省結構函數將不會被供給,這一點要留心。

 

3) 靜態結構函數

靜態結構函數是實現對一個類進行初始化的方法成員。它一般用於對靜態數據的初始化。靜態結構函數不能有參數,不能有潤飾符而且不能被調用,當類被加載時,類的靜態結構函數主動被調用。如:

using System.Data;
class Employee
{
private static DataSet ds;
static Employee()
{
ds = new DataSet(...);
}
...
}

聲明了一個有靜態結構函數的類Employee。留心靜態結構函數只能對靜態數據成員進行初始化,而不能對非靜態數據成員進行初始化。但是,非靜態結構函數既可以對靜態數據成員賦值,也可以對非靜態數據成員進行初始化。

假如類僅包含靜態成員,你可以創立一個private的結構函數:private TestClass() {…},但是private意味着從類的外面不可能拜訪該結構函數。所以,它不能被調用,且沒有對象可以被該類定義實例化。

以上是幾種類型結構函數的簡略應用,下面將重點先容一下在類的層次結構中(即持續結構中)基類和派生類的結構函數的應用方法。派生類對象的初始化由基類和派生類共同完成:基類的成員由基類的結構函數初始化,派生類的成員由派生類的結構函數初始化。

當創立派生類的對象時,系統將會調用基類的結構函數和派生類的結構函數,構 造函數的履行次序是:先履行基類的結構函數,再履行派生類的結構函數。假如派生類又有對象成員,則,先履行基類的結構函數,再履行成員對象類的結構函數,最后履行派生類的結構函數。

至於履行基類的什么結構函數,缺省情況下是履行基類的無參結構函數,假如要履行基類的有參結構函數,則必需在派生類結構函數的成員初始化表中指出。如:

class A
{ private int x;
public A( ) { x = 0; }
public A( int i ) { x = i; }
};

class B : A
{ private int y;
public B( ) { y = 0; }
public B( int i ) { y = i; }
public B( int i, int j ):A(i) { y = j; }
};

B b1 = new B(); //履行基類A的結構函數A(),再履行派生類的結構函數B()
B b2 = new B(1); //履行基類A的結構函數A(),再履行派生類的結構函數B(int)
B b3 = new B(0,1); //履行履行基類A的結構函數A(int) ,再履行派生類的

結構函數B(int,int)

在這里結構函數的履行次序是必定要分析明白的。另外,假如基類A中沒有供給無參結構函數public A( ) { x = 0; },則在派生類的所有結構函數成員初始化表中必需指出基類A的有參結構函數A(i),如下所示:

class A
{ private int x;
public A( int i ) { x = i; }
};

class B : A
{ private int y;
public B():A(i) { y = 0; }
public B(int i):A(i) { y = i; }

public B(int i, int j):A(i) { y = j; }
};

三.析構函數和垃圾回收器在C#中的應用

析構函數是實現燒毀一個類的實例的方法成員。析構函數不能有參數,不能任何潤飾符而且不能被調用。由於析構函數的目標與結構函數的相反,就加前綴‘~’以示差別。

固然C#(更確實的說是CLR)供給了一種新的內存治理機制---主動內存治理機制(Automatic memory management),資源的開釋是可以通過“垃圾回收器” 主動完成的,一般不需要用戶干涉,但在有些特別情況下還是需要用到析構函數的,如在C#中非托管資源的開釋。

資源的開釋一般是通過'垃圾回收器'主動完成的,但具體來說,仍有些需要留心的處所:

1. 值類型和引用類型的引用實在是不需要什么'垃圾回收器'來開釋內存的,由於當它們出了作用域后會主動開釋所占內存,由於它們都保留在棧(Stack)中;

2. 只有引用類型的引用所指向的對象實例才保留在堆(Heap)中,而堆由於是一個自由存儲空間,所以它並沒有像'棧'那樣有生存期('棧'的元素彈出后就代表生存期結束,也就代表開釋了內存),並且要留心的是,'垃圾回收器'只對這塊區域起作用;

然而,有些情況下,當需要開釋非托管資源時,就必需通過寫代碼的方法來解決。通常是應用析構函數開釋非托管資源,將用戶自己編寫的開釋非托管資源的代碼段放在析構函數中即可。需要留心的是,假如一個類中沒有應用到非托管資源,那么必定不要定義析構函數,這是由於對象履行了析構函數,那么'垃圾回收器'在開釋托管資源之前要先調用析構函數,然后第二次才真正開釋托管資源,這樣一來,兩次刪除動作的花銷比一次大多的。下面應用一段代碼來示析構函數是如何應用的:

public class ResourceHolder
{

~ResourceHolder()
{
// 這里是清算非托管資源的用戶代碼段
}

}

四.小結

結構函數與析構函數固然是一個類中情勢上較簡略的函數,但它們的應用決非看上往那么簡略,因此機動而准確的應用結構函數與析構函數能夠幫你更好的懂得CLR的內存治理機制,以及更好的治理系統中的資源。


免責聲明!

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



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