《隨筆十五》——C#中的 “ C#中的類定義、介面定義、遮蔽基類成員、 ”
目錄
C#中的類定義
class MyClass
{
//Members
}
● 這樣定義一個類後,就可以在專案中能訪問該定義的其他位置對該類進行例項化。 在預設情況下, 類是內部的(internal)—— 即只有當前工程中的程式碼能夠訪問。 可以使用關鍵字 internal 寫在 class 的前面 來顯式指定這一點。
● 還可以使用關鍵字public 指定類是公共的—— 即可以在任何專案工程中的程式碼來訪問。
● 還可以使用關鍵字abstract 指定類是抽象類(該類只能被繼承,不能被例項化,可以有抽象成員),抽象類 也可以是 internal 和 public 的 可訪問性。
● 還可以使用關鍵字 sealed 指定類是 密封類(該類不能被繼承,就是不能做基類)密封類 也可以是 internal 類 和 public 類 的 可訪問性。
class MyClass : base
{
//Members
}
上述程式碼在類定義中指定繼承,表示該類被描述為直接繼承自該基類。
● 注意: 除了特殊的類 object, 所有的類都是派生類, 即使它們沒有 顯式指定繼承的基類說明。 類object 是唯一的非派生類, 因為它是繼承層次結構的基礎。 所以說,如果沒有使用基類,被定義的類只繼承於基類 object。
注意: 在C# 中一個類只能有一個基類,稱為單繼承。
● 雖然類只能直接繼承一個基類, 但繼承的層次沒有限制。 也就說,作為基類的類可以派生自另外一個類, 而這個類又派生自另外一個類。 一直下去,直至最終到達object。
基類和派生類是相對的術語。 所有的類都是派生類,要麼派生自object, 要麼派生自其它的類。 所以, 通常當我們稱一個類為派生類時, 我們的意思是它直接派生自某類而不是object。
注意: 編譯器不允許派生類的可訪問性高於基類, 也就說, 內部類可以繼承於一個公共基類,但公共類不能繼承於一個內部基類。
介面的定義
● 使用關鍵字 interface 來定義介面,語法為:
interface myInterface
{
//interface members
}
● 在預設情況下, 介面是內部的(internal)—— 即只有當前工程中的程式碼能夠訪問。 可以使用關鍵字 internal 寫在 interface 的前面 來顯式指定這一點。
● 還可以使用關鍵字public 指定介面是公共的—— 即可以在任何專案工程中的程式碼來訪問。
● 不可以在介面中使用 abstract 和 sealed , 因為這兩個關鍵字在介面中是沒有意義的。
● 也可以使用 與類繼承類似的方式來指定介面的繼承。 主要區別是可以使用多個集介面:
interface IMyInterface : IMyBaseInterface, IMyBaseInterface2
{
// Interface members
}
介面不是類,所以沒有繼承object, 但是object 的成員 可以通過介面型別的變數來訪問。 如上所述, 不能用例項化類的方式來例項化介面。
● 除了可以在類的後面指定基類外, 還可以在冒號的後面指定支援的介面。 如果指定了基類,它必須緊跟冒號之後, 之後才是介面。 如果未指定基類, 介面就緊跟在冒號的後面。 必須使用逗號來分隔基類名 ( 如果有基類的話 ) 和介面名。
class MyClass : MyBase,IMyBaseInterface, IMyBaseInterface2
{
// Class members
}
上述的類指定了一個基類,兩個介面。 支援該介面的類必須實現所有介面的成員, 但如果不想使用給定的介面成員, 可以提供一種 “ 空” 的實現方式 (沒有函式程式碼), 還可以把介面成員實現為抽象類中的抽象成員。
遮蔽基類成員
● 有時候我們要繼承包含某個特殊方法的基類。 該方法雖然適合宣告它的類, 但卻不一定適合派生類。 在這種情況下, 我們希望在派生類中宣告新成員遮蔽基類中的方法
下面看一個示例程式,仔細觀察註釋 和程式碼
namespace HelloWorld_Console
{
class SomeClass
{
// 注意: 基類中的成員 只有保護的和公有的才會被派生類隱藏, 私有的成員不會被隱藏。
public string Field1 = " 基類中的欄位宣告.";
public void Method1()
{
WriteLine($"基類中的Method1方法");
}
public static int Field2;
}
class OtherClass : SomeClass
{
new public static double Field2; //還可以遮蔽靜態成員
// public int Field1 = 0;錯誤,雖然該欄位的返回型別不同,名稱跟基類中的相同,但是還是需要使用 new 前輟。
new public string Field1 = " 派生類中的欄位宣告.";
new public int Method1() // 雖然該成員函式的返回型別是int, 名稱跟基類中的一樣,但是還是需要前輟new
{// 通過在派生類中宣告新得帶有相同簽名的成員函式,那麼該派生類中的該函式會遮蔽掉基類中相同簽名的成員函式。
/* 注意: 成員函式的簽名由 名稱和引數列表組成, 不包含返回型別。如果派生類中的 Method1 該函式新增一個
形參, 就不需要前輟new, 因為它們兩個是不一樣的函式簽名 (C++ 中 與這裡不一樣) */
WriteLine($"派生類中的Method1方法");
return 0;
}
}
class Program
{
static void Main(string[] args)
{
OtherClass oc = new OtherClass();
oc.Method1();
ReadKey();
}
}
}
看一個基類引用的程式:
namespace HelloWorld_Console
{
class SomeClass
{
public string Field1 = " 基類中的欄位宣告.";
public void Method1()
{
WriteLine($"基類中的Method1方法");
}
public static int Field2;
}
class OtherClass : SomeClass
{
new public static double Field2; //還可以遮蔽靜態成員
// public int Field1 = 0;錯誤,雖然該欄位的返回型別不同,名稱跟基類中的相同,但是還是需要使用 new 前輟。
new public string Field1 = " 派生類中的欄位宣告.";
new public int Method1() // 雖然該成員函式的返回型別是int, 名稱跟基類中的一樣,但是還是需要前輟new
{// 通過在派生類中宣告新得帶有相同簽名的成員函式,那麼該派生類中的該函式會遮蔽掉基類中相同簽名的成員函式。
/* 注意: 成員函式的簽名由 名稱和引數列表組成, 不包含返回型別。如果派生類中的 Method1 該函式新增一個
形參, 就不需要前輟new, 因為它們兩個是不一樣的函式簽名 */
WriteLine($"派生類中的Method1方法");
return 0;
}
}
class Program
{
static void Main(string[] args)
{
OtherClass oc = new OtherClass();
oc.Method1(); // 呼叫的是派生類中的該函式
SomeClass mySomeClass = (SomeClass)oc; //是一個基類引用派生類物件
mySomeClass.Method1(); // 這裡呼叫的是基類中的該函式
ReadKey();
}
}
}
基類訪問
如果在派生類中想訪問被隱藏的基類中的繼承成員, 可以在派生類中 顯式用 base. 成員名 來訪問基類中被隱藏的成員。
namespace HelloWorld_Console
{
class SomeClass
{
public string Field1 = " 基類中的欄位宣告.";
public void Method1()
{
WriteLine($"基類中的Method1方法");
}
}
class OtherClass : SomeClass
{
new public string Field1 = " 派生類中的欄位宣告.";
new public void Method1()
{
WriteLine(Field1);
WriteLine(base.Field1); //訪問派生類中的欄位
}
}
class Program
{
static void Main(string[] args)
{
OtherClass oc = new OtherClass();
oc.Method1();
SomeClass mySomeClass = (SomeClass)oc; //是一個基類引用派生類物件
mySomeClass.Method1(); // 這裡呼叫的是基類中的該函式
ReadKey();
}
}
}
輸出結果為:
派生類中的欄位宣告.
基類中的欄位宣告.
基類中的Method1方法