更新: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 完全相同的方式來宣告。在介面定義中,屬性僅可指示其型別,而藉由單獨使用 get 和 set 關鍵字,來指示介面是唯讀、唯寫或是可讀寫。以下介面宣告了一個唯讀屬性:
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() 中一樣。