一.繼承的類型 在面向對象的編程中,有兩種截然不同繼承類型:實現繼承和介面繼承 1.實現繼承和介面繼承 *實現繼承:表示一個類型派生於基類型,它擁有該基類型的所有成員欄位和函數。在實現繼承中,派生類型採用基類型的每個函數的實現代碼,除非在派生類型的定義中指定某個函數的實現代碼。在需要給現有的類型添加 ...
一.繼承的類型
在面向對象的編程中,有兩種截然不同繼承類型:實現繼承和介面繼承
1.實現繼承和介面繼承
*實現繼承:表示一個類型派生於基類型,它擁有該基類型的所有成員欄位和函數。在實現繼承中,派生類型採用基類型的每個函數的實現代碼,除非在派生類型的定義中指定某個函數的實現代碼。在需要給現有的類型添加功能,或許多相關的類型共用一組重要的公共功能時,可以使用這種類型的繼承。
*介面繼承:表示一個類型只繼承了函數的簽名,沒有繼承任何的代碼。在需要指定該類型具有某些可用的特性時,最好使用這種類型的繼承。
2.多重繼承
C#不支持多重繼承,但C#允許類型派生自多個介面————多重介面繼承。這說明,C#類可以派生自另一個類和任意多個介面。更準確的說,因為System.Object是一個公共的基類,所以每個C#(除Object之外)都有一個基類,還可以有任意多個介面。
3.結構的繼承
使用結構的一個限制是結構不支持實現繼承,但每個結構都自動派生自System.ValueType。不能編碼實現類型層次的結構,但結構可以實現介面。
二.繼承的實現
語法:
class MyDreved:BaseClass
{
}
如果類或結構也派生自介面,則用逗號分隔列表中的基類和介面:
class MyDreved:BaseClass,IIntenface1,IIntenface2
{
}
如果在類定義中沒有指定基類,C#編譯器就假定System.Object是基類。
1.虛方法
把一個基類函數聲明為virtual,就可以在任何派生類中重寫(override)該函數:
class BaseClass
{
public virtual void VirtualMethod()
{
//
}
}
也可以把屬性聲明為virtual。對於虛屬性或重寫屬性,語法與非虛屬性相同,但要在定義中添加virtual關鍵字:
public virtual string Name
{
get;set;
}
C#中虛函數的概念與標準OOP的概念相同:可以在派生類中重寫虛函數。在調用方法時,會調用該派生類的合適方法。在C#中,函數預設情況下不是虛的,但(除了構造函數)可以顯式的聲明為virtual。
在派生類中重寫一個函數時,要使用override關鍵字顯示聲明:
class MyDreved: BaseClass
{
public override void VirtualMethod()
{
//
}
}
成員欄位和靜態函數都不能聲明為virtual,因為這個概念只對類中的實例函數成員有意義。
2.隱藏方法
如果簽名相同的方法在基類和派生類中都進行了聲明,但該方法沒有分別聲明為virtual和override,派生類方法就會隱藏基類方法。
class A { public void a() { Console.WriteLine('CLASS is A'); } } class B:A { public void a() { Console.WriteLine('CLASS is B'); } } class client { static void main() { B b=new B(); A a=b; a.a(); b.a(); } } /*輸出 CLASS IS A CLASS IS B */
在大多數情況下,是要重寫方法,而不是隱藏方法,因為隱藏方法會造成對於給定類的實例調用錯誤的方法。但是,C#語法會在編譯時收到這個潛在錯誤的警告。
在C#中,要隱藏一個方法應使用new 關鍵字聲明,這樣在編譯時就不會發出警告:
class A
{
public void a()
{
Console.WriteLine('CLASS is A');
}
}
class B:A
{
public new void a()
{
Console.WriteLine('CLASS is B');
}
}
3.調用函數的基類版本
C#可以從派生類中調用方法的基本版本:base.<MethodName>()
class MyDreved: BaseClass
{
public override void VirtualMethod()
{
base.VirtualMethod();
}
}
可以使用base.<MethodName>()語法調用基類中的任何方法,不必從同一方法的重載中調用它。
4.抽象類和抽象函數
C#允許把類和函數聲明為abstract.抽象類不能實例化,而抽象不能直接實現,必須在非抽象的派生類中重寫。顯然抽象函數也是虛擬的(儘管不需要提供virtual,實際上,也不能提供該關鍵字)。
如果類包含抽象函數,則該類也是抽象的,也必須聲明為抽象的:
abstract class Building
{
public abstract void Cal();
}
抽象類中不能聲明非抽象方法,但可以聲明其它的非抽象成員。
5.密封類和密封方法
C#允許把類和方法聲明為sealed。對於類,這表示不能繼承該類;對於方法,表示不能重寫該方法。
sealed class A
{
}
class B:A //報錯
{
}
如果基類上不希望有重寫的方法和屬性,就不要把它聲明為virtual.
6.派生類的構造函數
假定沒有為任何類定義任何顯示的構造函數,編譯器就會為所有的類提供預設的初始化構造函數,在後臺編譯器可以很好的解決類的層次結構中的問題,每個類中的每個欄位都會初始化為對應的預設值。
在創建派生類的實例時,實際上會有多個構造函數起作用。要實例化的類的構造函數本身不能初始化類,還必須調用基類中的構造函數。
構造函數的調用順序是先調用Object,在按照層次結構調用基類的構造函數,由基類到父類,直到到達要實例化的類為止。在這個過程中,每個構造函數都初始化它自己的類中的欄位。因為最先調用的總是基類的構造函數,所以派生類在執行過程中可以訪問任何基類的成員,因為基類已經構造出來了,其欄位也初始化了。
*在層次結構中添加無參數的構造函數
在層次結構中添加一個無參數的構造函數會替換預設的構造函數,所以在執行過程中,會預設調用基類中添加的無參數的構造函數。其它方面不變。
*在層次結構中添加帶參數的構造函數
在層次結構中要調用這個帶參數的構造函數,需要在父類的構造函數中顯示調用:
public abstract class GenericCustomer { private string name; public GenericCustomer() { name = "<no name>"; } public GenericCustomer(string name) { this.name = name; } public string Name { get {return name;} set {name = value;} } } public class Nevermore60Customer : GenericCustomer { private string referrerName; private uint highCostMinutesUsed; ublic Nevermore60Customer(string name) : this(name, " <None>") { } public Nevermore60Customer(string name, string referrerName) : base(name) { this.referrerName = referrerName; } public string ReferrerName { get {return referrerName;} set {referrerName = value;} } }
三. 修飾符
修飾符可以指定方法的可見性:如public或private,還可以指定一項的本質,如方法是virtual或abstract.
1.可見性修飾符
修飾符 應用於 說明
public 所有類和成員 任何代碼可以訪問
protected 類的成員和內嵌類 只有在類內部和派生類中訪問
internal 所有類和成員 只有在類內部和包含它的程式集中訪問
private 類的成員和內嵌類 只有在類內部訪問
protected internal 類的成員和內嵌類 只有在類內部,派生類中和包含它的程式集中訪問
不能把類定義為protected,private,protected internal,因為這些修飾符對於包含在名稱空間中的類型沒有意義。因此這些修飾符只能應用於成員。但是可以用這些修飾符定義嵌套的類(內嵌類,包含在其它類中的類),因為在這種情況下,類也具有成員的狀態:
public class OuterClass
{
protected class InnerClass
{
}
}
2.其它修飾符
修飾符 應用於 說明
new 函數 隱藏函數
static 所有成員 靜態
virtual 函數 成員可以由派生類重寫
abstract 類,函數 抽象
override 函數 重寫虛擬和抽象的成員
sealed 類,方法,屬性 不能繼承和重寫
extern 僅靜態方法 成員在外部用另一種語言實現
四.介面
public interface IDisposable
{
void Dispose();
}
聲明介面在語法上和聲明抽象類完全相同,但不允許提供任何成員的實現方式。抽象類可以提供除方法之外的其它成員的實現方式,比如屬性。
一般情況下,介面只能包含方法,屬性,索引器和事件的聲明。
不能實例化介面,介面即不能有構造函數,也不能有欄位。介面定義也不允許包含運算符重載。
在介面中不允許聲明關於成員的修飾符。介面成員總是公有的,不能聲明為虛擬和靜態。如果需要,在實現的類中聲明。
實現介面的類必須實現介面的所有成員。
介面可以彼此繼承,其方式與類的繼承方式相同。