程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 深入剖析C#繼承機制

深入剖析C#繼承機制

編輯:C#入門知識

一. 繼承基礎知識


  為了提高軟件模塊的可復用性和可擴充性,以便提高軟件的開發效率,我們總是希望能夠利用前人或自己以前的開發成果,同時又希望在自己的開發過程中能夠有足夠的靈活性,不拘泥於復用的模塊。C#這種完全面向對象的程序設計語言提供了兩個重要的特性--繼承性inheritance 和多態性polymorphism。


  繼承是面向對象程序設計的主要特征之一,它可以讓您重用代碼,可以節省程序設計的時間。繼承就是在類之間建立一種相交關系,使得新定義的派生類的實例可以繼承已有的基類的特征和能力,而且可以加入新的特性或者是修改已有的特性建立起類的新層次。


  現實世界中的許多實體之間不是相互孤立的,它們往往具有共同的特征也存在內在的差別。人們可以采用層次結構來描述這些實體之間的相似之處和不同之處。



圖1 類圖


  上圖反映了交通工具類的派生關系。最高層的實體往往具有最一般最普遍的特征,越下層的事物越具體,並且下層包含了上層的特征。它們之間的關系是基類與派生類之間的關系。


  為了用軟件語言對現實世界中的層次結構進行模型化,面向對象的程序設計技術引入了繼承的概念。一個類從另一個類派生出來時,派生類從基類那裡繼承特性。派生類也可以作為其它類的基類。從一個基類派生出來的多層類形成了類的層次結構。


  注意:C#中,派生類只能從一個類中繼承。這是因為,在C++中,人們在大多數情況下不需要一個從多個類中派生的類。從多個基類中派生一個類這往往會帶來許多問題,從而抵消了這種靈活性帶來的優勢。
C#中,派生類從它的直接基類中繼承成員:方法、域、屬性、事件、索引指示器。除了構造函數和析構函數,派生類隱式地繼承了直接基類的所有成員。看下面示例:
using System ;
class Vehicle //定義交通工具(汽車)類
{
protected int wheels ; //公有成員:輪子個數
protected float weight ; //保護成員:重量
public Vehicle( ){;}
public Vehicle(int w,float g){
wheels = w ;
weight = g ;
}
public void Speak( ){
Console.WriteLine( "交通工具的輪子個數是可以變化的! " ) ;
}
} ;
class Car:Vehicle //定義轎車類:從汽車類中繼承
{
int passengers ; //私有成員:乘客數
public Car(int w , float g , int p) : base(w, g)
{
wheels = w ;
weight = g ;
passengers=p ;
}
}


  Vehicle 作為基類,體現了"汽車"這個實體具有的公共性質:汽車都有輪子和重量。Car 類繼承了Vehicle 的這些性質,並且添加了自身的特性:可以搭載乘客。






二、C#中的繼承符合下列規則:


  1、繼承是可傳遞的。如果C從B中派生,B又從A中派生,那麼C不僅繼承了B中聲明的成員,同樣也繼承了A中的成員。Object 類作為所有類的基類。


  2、派生類應當是對基類的擴展。派生類可以添加新的成員,但不能除去已經繼承的成員的定義。


  3、構造函數和析構函數不能被繼承。除此以外的其它成員,不論對它們定義了怎樣的訪問方式,都能被繼承。基類中成員的訪問方式只能決定派生類能否訪問它們。


  4、派生類如果定義了與繼承而來的成員同名的新成員,就可以覆蓋已繼承的成員。但這並不因為這派生類刪除了這些成員,只是不能再訪問這些成員。


  5、類可以定義虛方法、虛屬性以及虛索引指示器,它的派生類能夠重載這些成員,從而實現類可以展示出多態性。


  6、派生類只能從一個類中繼承,可以通過接呂實現多重繼承。


  下面的代碼是一個子類繼承父類的例子:



using System ;
public class ParentClass
{
public ParentClass( )
{ Console.WriteLine("父類構造函數。"); }
public void print( )
{ Console.WriteLine("Im a Parent Class。") ; }
}
public class ChildClass : ParentClass
{
public ChildClass( )
{ Console.WriteLine("子類構造函數。") ; }
public static void Main( ) {
ChildClass child = new ChildClass( ) ;
child.print( ) ;
}
}


  程序運行輸出:


  父類構造函數。子類構造函數。Im a Parent Class。


  上面的一個類名為ParentClass, main函數中用到的類名為ChildClass。要做的是創建一個使用父類ParentClass現有代碼的子類ChildClass。


  1.首先必須說明ParentClass是ChildClass的基類。


  這是通過在ChildClass類中作出如下說明來完成的:"public class ChildClass : ParentClass"。在派生類標識符後面,用分號":" 來表明後面的標識符是基類。C#僅支持單一繼承。因此,你只能指定一個基類。


  2.ChildClass的功能幾乎等同於ParentClass。


  因此,也可以說ChildClass "就是" ParentClass。在ChildClass 的Main( )方法中,調用print( ) 方法的結果,就驗證這一點。該子類並沒有自己的print( )方法,它使用了ParentClass中的 print( )方法。在輸出結果中的第三行可以得到驗證。


  3.基類在派生類初始化之前自動進行初始化。ParentClass 類的構造函數在ChildClass的構造函數之前執行。





三. 訪問與隱藏基類成員


  (1) 訪問基類成員


  通過base 關鍵字訪問基類的成員:


   調用基類上已被其他方法重寫的方法。
   指定創建派生類實例時應調用的基類構造函數。
   基類訪問只能在構造函數、實例方法或實例屬性訪問器中進行。
   從靜態方法中使用 base 關鍵字是錯誤的。


  示例:下面程序中基類 Person 和派生類 Employee 都有一個名為 Getinfo 的方法。通過使用 base 關鍵字,可以從派生類中調用基類上的 Getinfo 方法。



using System ;
public class Person
{
protected string ssn = "111-222-333-444" ;
protected string name = "張三" ;
public virtual void GetInfo() {
Console.WriteLine("姓名: {0}", name) ;
Console.WriteLine("編號: {0}", ssn) ;
}
}
class Employee: Person
{
public string id = "ABC567EFG23267" ;
public override void GetInfo() {
// 調用基類的GetInfo方法:
base.GetInfo();
Console.WriteLine("成員ID: {0}", id) ;
}
}
class TestClass {
public static void Main() {
Employee E = new Employee() ;
E.GetInfo() ;
}
}


  程序運行輸出:


   姓名: 張三
   編號: 111-222-333-444
   成員ID: ABC567EFG23267
   示例:派生類同基類進行通信。



using System ;
public class Parent
{
string parentString;
public Parent( )
{ Console.WriteLine("Parent Constructor.") ; }
public Parent(string myString) {
parentString = myString;
Console.WriteLine(parentString) ;
}
public void print( )
{ Console.WriteLine("Im a Parent Class.") ; }
}
public class Child : Parent
{
public Child( ) : base("From Derived")
{ Console.WriteLine("Child Constructor.") ; }
public void print( ) {
base.print( ) ;
Console.WriteLine("Im a Child Class.") ;
}
public static void Main( ) {
Child child = new Child( ) ;
child.print( ) ;
((Parent)child).print( ) ;
}
}


  程序運行輸出:


From Derived
Child Constructor.
Im a Parent Class.
Im a Child Class.
Im a Parent Class.


  說明:


  1.派生類在初始化的過程中可以同基類進行通信。


  上面代碼演示了在子類的構造函數定義中是如何實現同基類通信的。分號":"和關鍵字base用來調用帶有相應參數的基類的構造函數。輸出結果中,第一行表明:基類的構造函數最先被調用,其實在參數是字符串"From Derived"。


  2.有時,對於基類已有定義的方法,打算重新定義自己的實現。


  Child類可以自己重新定義print( )方法的實現。Child的print( )方法覆蓋了Parent中的 print 方法。結果是:除非經過特別指明,Parent類中的print方法不會被調用。


  3.在Child類的 print( ) 方法中,我們特別指明:調用的是Parent類中的 print( ) 方法。


  方法名前面為"base",一旦使用"base"關鍵字之後,你就可以訪問基類的具有公有或者保護權限的成員。 Child類中的print( )方法的執行結果出現上面的第三行和第四行。


  4.訪問基類成員的另外一種方法是:通過顯式類型轉換。


  在Child類的Main( )方法中的最後一條語句就是這麼做的。記住:派生類是其基類的特例。這個事實告訴我們:可以在派生類中進行數據類型的轉換,使其成為基類的一個實例。上面代碼的最後一行實際上執行了Parent類中的 print( )方法。





2) 隱藏基類成員


  想想看,如果所有的類都可以被繼承,繼承

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved