共用方式為


繼承和衍生類別 (C# 與 Java 的比較)

更新:2007 年 11 月

您可以建立衍生自現有類別的新類別,藉此擴充現有類別的功能。這個衍生類別 (Derived Class) 會繼承基底類別的屬性,您可以依需要加入或覆寫方法和屬性。

在 C# 中,繼承和介面實作都是由 : 運算子定義,此運算子就等同於 Java 中的 extends 和 implements。基底類別在類別宣告中應該一直位於最左側。

C# 和 Java 一樣,都不支援多重繼承,也就是說類別無法繼承自一個以上類別。不過,可以和在 Java 中一樣,使用介面來達成這個目的。

下列程式碼使用兩個私用 (Private) 成員變數 x 和 y (代表點的位置) 來定義名為 CoOrds 的類別。這些變數可分別透過名為 X 和 Y 的屬性來存取:

public class CoOrds
{
    private int x, y;

    public CoOrds()  // constructor
    {
        x = 0;
        y = 0;
    }

    public int X
    {
        get { return x; }
        set { x = value; }
    }

    public int Y
    {
        get { return y; }
        set { y = value; }
    }
}

請從 CoOrds 類別衍生出一個名為 ColorCoOrds 的新類別,如下所示:

public class ColorCoOrds : CoOrds

於是 ColorCoOrds 會繼承此基底類別的所有欄位和方法,您可以在其中加入新的欄位和方法,如此即可在這個衍生類別中依需要提供其他的功能。在這個範例中,加入了私用成員和存取子,以便將色彩加入至該類別:

public class ColorCoOrds : CoOrds
{
    private System.Drawing.Color screenColor;


    public ColorCoOrds()  // constructor
    {
        screenColor = System.Drawing.Color.Red;
    }

    public System.Drawing.Color ScreenColor
    {
        get { return screenColor; }
        set { screenColor = value; }
    }
}

衍生類別的建構函式隱含呼叫基底類別 (或 Java 用語中的 Superclass) 的建構函式。繼承時,所有基底類別建構函式會在衍生類別的建構函式之前,依照類別在類別階層中出現的順序來進行呼叫。

轉換型別成基底類別

和在 Java 中一樣,您無法使用基底類別參考來存取衍生類別的成員和方法,即使基底類別參考可能包含了衍生型別之物件的有效參考。

可利用衍生型別參考以隱含方式來參考衍生類別:

ColorCoOrds color1 = new ColorCoOrds();
CoOrds coords1 = color1;

在這個程式碼中,基底類別參考 coords1 包含 color1 參考的複本。

base 關鍵字

您可以存取子類別 (Subclass) 中的基底類別成員,即使那些基底成員已在 Superclass 中使用 base 關鍵字覆寫。例如,您可以建立衍生類別,其方法簽章與基底類別中的簽章相同。如果在該方法之前放置 new 關鍵字,就是在指示這是屬於衍生類別的全新方法。此時仍可使用 base 關鍵字來提供方法,以存取基底類別中的原始方法。

例如,假設基底 CoOrds 類別有個名為 Invert() 的方法,這個方法是用來交換 x 和 y 座標。您可以使用如下的程式碼,替代衍生 ColorCoOrds 類別中的這個方法:

public new void Invert()
{
    int temp = X;
    X = Y;
    Y = temp;
    screenColor = System.Drawing.Color.Gray;
}

如您所見,這個方法會交換 x 和 y,然後再將座標的色彩設為灰色。您可以在 ColorCoOrds 中建立另一個方法來存取這個方法的基底實作,如下列程式碼所示:

public void BaseInvert()
{
    base.Invert();
}

接著,呼叫 BaseInvert() 方法,藉此叫用 ColorCoOrds 物件上的基底方法。

ColorCoOrds color1 = new ColorCoOrds();
color1.BaseInvert();

請記住,如果將基底類別參考指派給 ColorCoOrds 的執行個體,然後再存取它的方法,那麼您將取得相同的結果:

CoOrds coords1 = color1;
coords1.Invert();

選取建構函式

基底類別物件永遠在任何衍生類別之前建構完成。因此,基底類別的建構函式會在衍生類別的建構函式之前執行。如果基底類別擁有一個以上建構函式,衍生類別可決定要呼叫哪一個建構函式。例如,您可以修改 CoOrds 類別以便加入第二個建構函式,如以下所示:

public class CoOrds
{
    private int x, y;

    public CoOrds()
    {
        x = 0;
        y = 0;
    }

    public CoOrds(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}

接著,可以變更 ColorCoOrds 類別,以便利用 base 關鍵字來使用其中某一個可用的建構函式。

public class ColorCoOrds : CoOrds
{
    public System.Drawing.Color color;

    public ColorCoOrds() : base ()
    {
        color = System.Drawing.Color.Red;
    }

    public ColorCoOrds(int x, int y) : base (x, y)
    {
        color = System.Drawing.Color.Red;
    }
}

在 Java 中,是使用 super 關鍵字來實作這項功能。

方法覆寫

衍生類別為已宣告的方法提供新的實作時,可能會覆寫基底類別中的方法。Java 和 C# 兩者之間的一項重大差異,就是 Java 方法是預設標記為虛擬,而 C# 中的方法則必須使用 virtual 修飾詞 (Modifier) 明確標記為虛擬。屬性存取子以及方法可以使用幾乎相同的方式來覆寫。

虛擬方法

要在衍生類別中覆寫的方法是使用 virtual 修飾詞來進行宣告。在衍生類別中,覆寫方法是使用 override 修飾詞來進行宣告。

override 修飾詞代表衍生類別的方法或屬性,它會取代基底類別中具有相同名稱和簽章的方法或屬性。要覆寫的基底方法必須宣告為 virtual、abstract 或 override:您不能以這個方式覆寫非虛擬方法或靜態方法。可以被覆寫 (Overridden) 與進行覆寫 (Overriding) 的方法或屬性,都必須擁有相同存取層級的修飾詞。

在下列程式碼中,示範了名為 StepUp 的虛擬方法,您是在衍生類別中使用 override 修飾詞來覆寫這個虛擬方法:

public class CountClass 
{
    public int count; 

    public CountClass(int startValue)  // constructor
    {
        count = startValue;
    }

    public virtual int StepUp()
    {
        return ++count;
    }
}

class Count100Class : CountClass 
{
    public Count100Class(int x) : base(x)  // constructor 
    {
    }

    public override int StepUp()
    {
        return ((base.count) + 100);
    }
}

class TestCounters
{
    static void Main()
    {
        CountClass counter1 = new CountClass(1);
        CountClass counter100 = new Count100Class(1);

        System.Console.WriteLine("Count in base class = {0}", counter1.StepUp());
        System.Console.WriteLine("Count in derived class = {0}", counter100.StepUp());
    } 
}

執行這個程式碼時,會看到衍生類別的建構函式是使用基底類別中所指定的方法主體,這可讓您不需複製該程式碼便能初始化這個 count 成員。輸出如下:

Count in base class = 2

Count in derived class = 101

抽象類別

抽象類別會將一個或多個方法或屬性宣告為抽象。在宣告這類方法的類別中並沒有提供此類方法的實作,雖然抽象類別也可以包含非抽象方法 (它就是接受實作的方法)。抽象類別無法直接執行個體化,而且只能做為衍生類別。除非衍生成員宣告本身為抽象,否則這種衍生類別必須使用 override 關鍵字來提供所有抽象方法和抽象屬性的實作。

下列範例將宣告抽象 Employee 類別。您同時也會建立名為 Manager 的衍生類別,此類別提供 Employee 類別中所定義之抽象 Show() 方法的實作。

public abstract class Employee 
{ 
    protected string name;

    public Employee(string name)  // constructor
    { 
        this.name = name;
    }

    public abstract void Show();  // abstract show method
}

public class Manager: Employee
{ 
    public Manager(string name) : base(name) {}  // constructor

    public override void Show()  //override the abstract show method
    {
        System.Console.WriteLine("Name : " + name);
    }
}

class TestEmployeeAndManager
{ 
    static void Main()
    { 
        // Create an instance of Manager and assign it to a Manager reference:
        Manager m1 = new Manager("H. Ackerman");
        m1.Show();

        // Create an instance of Manager and assign it to an Employee reference:
        Employee ee1 = new Manager("M. Knott");
        ee1.Show();  //call the show method of the Manager class
    } 
}

這個程式碼會叫用 Manager 類別所提供的 Show() 實作,並且在螢幕上列印員工姓名。輸出如下:

Name : H. Ackerman

Name : M. Knott

介面

介面是一種包含方法簽章,但不包含方法實作的基本架構類別。如此一來,介面就像是只包含抽象方法的抽象類別。C# 介面與 Java 介面十分類似,它們以幾乎完全相同的方式運作。

根據定義,介面的所有成員都是公用的,而且介面不能包含常數、欄位 (私用資料成員)、建構函式、解構函式或任何型別的靜態成員。如果介面成員指定了任何修飾詞,編譯器將會產生錯誤。

您可以從介面衍生類別,以便實作該介面。除非宣告為抽象,否則這種衍生類別必須提供介面上所有方法的實作。

介面會採用與 Java 完全相同的方式來宣告。在介面定義中,屬性僅可指示其型別,而藉由單獨使用 getset 關鍵字,來指示介面是唯讀、唯寫或是可讀寫。以下介面宣告了一個唯讀屬性:

public interface ICDPlayer
{ 
    void Play();  // method signature
    void Stop();  // method signature

    int FastForward(float numberOfSeconds);

    int CurrentTrack  // read-only property
    {
        get;
    } 
}

藉由在 Java 的 implements 關鍵字位置上使用冒號,可從這個介面繼承類別。實作類別必須提供所有方法的定義,以及任何所需的屬性存取子,如下所示:

public class CDPlayer : ICDPlayer
{
    private int currentTrack = 0;

    // implement methods defined in the interface
    public void Play()
    {
        // code to start CD...
    }

    public void Stop()
    {
        // code to stop CD...
    }

    public int FastForward(float numberOfSeconds)
    {
        // code to fast forward CD using numberOfSeconds...

        return 0;  //return success code
    }

    public int CurrentTrack  // read-only property
    { 
        get 
        { 
            return currentTrack; 
        } 
    }

    // Add additional methods if required...
}

實作多重介面

類別可使用下列語法來實作多重介面:

public class CDAndDVDComboPlayer : ICDPlayer, IDVDPlayer

如果類別實作一個以上介面,而且介面中的成員名稱語意模糊,那麼它將使用屬性名稱或方法名稱的完整限定詞 (Qualifier) 來進行解析。換言之,衍生類別可藉由使用方法的完整名稱來解析衝突,以便指出方法所屬的介面,如同在 ICDPlayer.Play() 中一樣。

請參閱

概念

C# 程式設計手冊

參考

繼承 (C# 程式設計手冊)

其他資源

Java 開發人員可用的 C# 程式設計語言