在這篇文章中,我們會用示例討論C#繼承。繼承是面向對象編程的一項原則,這項原則解決了可擴展性問題。在這篇文章中,我們會討論以下幾點:
1、什么是繼承?
2、繼承的類型;
3、為什么需要繼承?
4、如何在應用程序中使用繼承?
一、繼承是什么?
從已存在的類中創建一個新類,這樣新類就獲得了已存在類的所有屬性和行為,這一過程就叫做繼承。傳送屬性(或行為)的類,叫做超類或父類或基類,而自超類繼承屬性或行為的類叫做子類或派生類(derived class)。總之一句話,繼承意味着從已經完成或已經現有的可利用的方面取得某些東西。
繼承是一種代碼可復用性和可改變性為目的的概念。這里所指的可改變性意味着可以重載對象的已有功能或特征,或是給對象添加更多的功能。
C#.NET支持的繼承類別
C#.NET將繼承划分為兩大類:
1、實現繼承(Implementation inheritance)
2、接口繼承(Interface inheritance)
二、繼承的類型
繼承分為5種類型,它們分別如下:
1、單一繼承(Single Inheritance):當一個類是從單個基類繼承而來,這種繼承關系叫單一繼承;

2、多級繼承(Multilevel Inheritance):當一個派生類是從另一個派生類創建的,這種繼承關系叫多級繼承;

3、同級繼承(Hierarchical Inheritance):當多個派生類是從同一基類創建的,這種繼承關系叫同級繼承;

4、混合繼承(Hybrid Inheritance):混合繼承是任意單繼承、同級繼承和等級繼承的組合;

5、多重繼承(Multiple Inheritance):當一個派生類創建於多個基類,這種繼承類型叫多重繼承。但是在.NET中,類是不能多重繼承的,但是接口可以多重繼承的。

注:處理由多重繼承引起的復雜性是非常復雜的,因此在.net中,類是不支持多重繼承的,而接口可以支持多重繼承。
C#中使用繼承需要考慮的規則
規則1:在繼承中,對於子類來說,父類的構造函數必須易於訪問,否則繼承就不成立,因為當我們創建子類對象時,它會運行並調用父類的構造函數,這樣父類變量會被初始化,我們就可以在子類中使用它們。
示例如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Drawing; 6 using System.Windows.Forms; 7 8 namespace parent_and_child_class 9 { 10 //創建父類Person和子類Student 11 public class Person 12 { 13 public Person() 14 { 15 Console.WriteLine("我是人"); 16 } 17 } 18 19 public class Student : Person 20 { 21 public Student() 22 { 23 Console.WriteLine("我是學生"); 24 } 25 } 26 27 //在客戶端通過子類無參構造函數創建子類實例 28 class Program 29 { 30 static void Main(string[] args) 31 { 32 Student student = new Student(); 33 34 Console.ReadKey(); 35 } 36 } 37 }

結論:通過調用子類無參構造函數創建子類實例,會默認調用父類無參構造函數。
規則2:在繼承中,子類能使用父類成員,但父類不能完全使用在子類中定義的成員。
注: 我們習慣認為,子類對父類是不可見的,或者說父類不知道子類的存在,所以父類是調用不到子類的方法、屬性的。
規則3:一個類的對象可以做為引用被分配給同一個類的變量,一個類的對象也可以做為引用分配給父類的變量,這樣引用開始分配給它的對象內存,但是現在也可以使用我們控制的引用來訪問子類的純成員。
注:父類對象不能分配給子類變量。通過顯式轉換,用子類對象創建的父類引用可以轉換回子類引用。
為什么我們需要繼承?
我們用示例來理解為什么需要繼承。假設一家公司有“n”個分支機構,要求將公司分支機構詳細信息電腦化,然后我們創建Class Branch類,此類包含data成員BranchCode, BranchName, 和 BranchAddress;還有函數 GetBranchData() 和 DisplayBranchData().。
一段時間后,公司還要求將每個分支機構的雇員詳細信息電腦化。然后我們創建了Class Employee,此類包含成員 EmployeeId, EmployeeName, EmployeeAddress, EmployeeAge,還有函數 GetEmployeeData() 和 DisplayEmployeeData().。
如果我們在沒有使用繼承的情況下創建這兩個類,我們需要獨立地分別地為每個類創建對象,像下面這樣:

Obj1是Class Branch類的對象,Obj2是Employee類的對象。很難分辨哪個employee屬於哪個分支機構。所以如果我們從Branch類派生出Employee類,我們再創建派生類Employee的對象,它會代表兩個類,會保持對基類和派生類成員的引用。

示例:
1 namespace InheritanceDemo 2 { 3 class Branch 4 { 5 int BranchCode; 6 string BranchName, BranchAddress; 7 public void GetBranchData() 8 { 9 Console.WriteLine("ENTER BRANCH DETAILS:"); 10 Console.WriteLine("ENTER BRANCH CODE"); 11 BranchCode = int.Parse(Console.ReadLine()); 12 Console.WriteLine("ENTER BRANCH NAME"); 13 BranchName = Console.ReadLine(); 14 Console.WriteLine("ENTER BRANCH ADDRESS"); 15 BranchAddress = Console.ReadLine(); 16 } 17 public void DisplayBranchData() 18 { 19 Console.WriteLine("BRANCH CODE IS : " + BranchCode); 20 Console.WriteLine("BRANCH NAME IS : " + BranchName); 21 Console.WriteLine("BRANCH ADDRESS IS : " + BranchAddress); 22 } 23 } 24 class Employee : Branch 25 { 26 int EmployeeId, EmployeeAge; 27 string EmployeeName, EmployeeAddress; 28 public void GetEmployeeData() 29 { 30 Console.WriteLine("ENTER EMPLYEE DETAILS:"); 31 Console.WriteLine("ENTER EMPLOYEE ID"); 32 EmployeeId = int.Parse(Console.ReadLine()); 33 Console.WriteLine("ENTER EMPLOYEE AGE"); 34 EmployeeAge = int.Parse(Console.ReadLine()); 35 Console.WriteLine("ENTER EMPLOYEE NAME"); 36 EmployeeName = Console.ReadLine(); 37 Console.WriteLine("ENTER EMPLOYEE ADDRESS"); 38 EmployeeAddress = Console.ReadLine(); 39 } 40 public void DisplayEmployeeData() 41 { 42 Console.WriteLine("EMPLOYEE ID IS : " + EmployeeId); 43 Console.WriteLine("EMPLOYEE NAME IS : " + EmployeeName); 44 Console.WriteLine("EMPLOYEE ADDRESS IS : " + EmployeeAddress); 45 Console.WriteLine("EMPLOYEE AGE IS : " + EmployeeAge); 46 } 47 } 48 class Program 49 { 50 static void Main(string[] args) 51 { 52 Employee obj1 = new Employee(); 53 obj1.GetBranchData(); 54 obj1.GetEmployeeData(); 55 obj1.DisplayBranchData(); 56 obj1.DisplayEmployeeData(); 57 Console.WriteLine("Press any key to exist."); 58 Console.ReadKey(); 59 } 60 } 61 }
在上面的例子中,我們將Branch類的GetEmployeeData() 和 DisplayEmployeeData() 設置為public,是因為外部的Employee類可以訪問這些函數。數據字段BranchCode, BranchName, 和 BranchAddress設置為private(默認),這樣只有在同一個類內才可以訪問。
但是,如果不想讓非繼承類訪問基類成員(在此例子中是Program類),而對於繼承類(Employee類)來說可以訪問基類,這時我們可以使用protected關鍵詞。
將public更換為protected后,示例如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Drawing; 6 using System.Windows.Forms; 7 8 namespace Employee_and_Branch 9 { 10 class Branch 11 { 12 int BranchCode; 13 string BranchName, BranchAddress; 14 protected void GetBrandhData() 15 { 16 Console.WriteLine("enter branch details"); 17 Console.WriteLine("enter branch code"); 18 BranchCode = int.Parse(Console.ReadLine()); 19 Console.WriteLine("enter branch name"); 20 BranchName = Console.ReadLine(); 21 Console.WriteLine("enter branch address"); 22 BranchAddress = Console.ReadLine(); 23 } 24 protected void DisplayBranchData() 25 { 26 Console.WriteLine("branch code is :" + BranchCode); 27 Console.WriteLine("branch name is :" + BranchName); 28 Console.WriteLine("branch address is :" + BranchAddress); 29 } 30 } 31 32 class Employee : Branch 33 { 34 int EmployeeId, EmployeeAge; 35 string EmployeeName, EmployeeAddress; 36 public void GetEmployeeData() 37 { 38 Console.WriteLine("enter emplyee details"); 39 Console.WriteLine("enter employee id"); 40 EmployeeId = int.Parse(Console.ReadLine()); 41 Console.WriteLine("enter employee age"); 42 EmployeeAge = int.Parse(Console.ReadLine()); 43 Console.WriteLine("enter employee name"); 44 EmployeeName = Console.ReadLine(); 45 Console.WriteLine("enter employee address"); 46 EmployeeAddress = Console.ReadLine(); 47 } 48 public void DisplayEmployeeData() 49 { 50 Console.WriteLine("employee id is :" + EmployeeId); 51 Console.WriteLine("employee name is :" + EmployeeName); 52 Console.WriteLine("employee address is :" + EmployeeAddress); 53 Console.WriteLine("employee age is :" + EmployeeAge); 54 } 55 } 56 57 class Program 58 { 59 static void Main(string[] args) 60 { 61 Employee obj1 = new Employee(); 62 obj1.GetBrandhData(); 63 obj1.GetEmployeeData(); 64 obj1.DisplayBranchData(); 65 obj1.DisplayEmployeeData(); 66 Console.WriteLine("press any key to exist."); 67 Console.ReadKey(); 68 } 69 } 70 }
以上代碼會報錯,具體如下:

1 namespace InheritanceDemo 2 { 3 class Branch 4 { 5 int BranchCode; 6 string BranchName, BranchAddress; 7 protected void GetBranchData() 8 { 9 Console.WriteLine("ENTER BRANCH DETAILS:"); 10 Console.WriteLine("ENTER BRANCH CODE"); 11 BranchCode = int.Parse(Console.ReadLine()); 12 Console.WriteLine("ENTER BRANCH NAME"); 13 BranchName = Console.ReadLine(); 14 Console.WriteLine("ENTER BRANCH ADDRESS"); 15 BranchAddress = Console.ReadLine(); 16 } 17 protected void DisplayBranchData() 18 { 19 Console.WriteLine("BRANCH CODE IS : " + BranchCode); 20 Console.WriteLine("BRANCH NAME IS : " + BranchName); 21 Console.WriteLine("BRANCH ADDRESS IS : " + BranchAddress); 22 } 23 } 24 class Employee : Branch 25 { 26 int EmployeeId, EmployeeAge; 27 string EmployeeName, EmployeeAddress; 28 public void GetEmployeeData() 29 { 30 //to call the base class method use base keyword 31 base.GetBranchData(); 32 Console.WriteLine("ENTER EMPLYEE DETAILS:"); 33 Console.WriteLine("ENTER EMPLOYEE ID"); 34 EmployeeId = int.Parse(Console.ReadLine()); 35 Console.WriteLine("ENTER EMPLOYEE AGE"); 36 EmployeeAge = int.Parse(Console.ReadLine()); 37 Console.WriteLine("ENTER EMPLOYEE NAME"); 38 EmployeeName = Console.ReadLine(); 39 Console.WriteLine("ENTER EMPLOYEE ADDRESS"); 40 EmployeeAddress = Console.ReadLine(); 41 } 42 public void DisplayEmployeeData() 43 { 44 base.DisplayBranchData(); 45 Console.WriteLine("EMPLOYEE ID IS : " + EmployeeId); 46 Console.WriteLine("EMPLOYEE NAME IS : " + EmployeeName); 47 Console.WriteLine("EMPLOYEE ADDRESS IS : " + EmployeeAddress); 48 Console.WriteLine("EMPLOYEE AGE IS : " + EmployeeAge); 49 } 50 } 51 class Program 52 { 53 static void Main(string[] args) 54 { 55 Employee obj1 = new Employee(); 56 //Here we cannot access the Branch class method as they are now protected 57 // obj1.GetBranchData(); //Will give Compile time error 58 obj1.GetEmployeeData(); 59 // obj1.DisplayBranchData(); // will give compile time error 60 obj1.DisplayEmployeeData(); 61 Console.WriteLine("Press any key to exist."); 62 Console.ReadKey(); 63 } 64 } 65 }
以上是正確的示例代碼。
一般地,當我們開發應用程序的時候,遵循以下過程:
1、識別與應用程序相關聯的實體;
2、識別與應用程序相關聯的特性;
3、現在,按層次順序分離每個實體的屬性,而不存在任何重復項;
4、將這些實體轉換為類;
示例:
我們用一個運行時的例子來了解一下繼承。假設我們正在為學校開發應用程序,實體的屬性如下所示:

現在,基於以下層級關系來分離實體的屬性。

現在定義表示實體的類,如下所示
1 namespace InheritanceDemo 2 { 3 public class Person 4 { 5 int Id; 6 string Name; 7 string Address; 8 string Phone; 9 } 10 public class Student : Person 11 { 12 string Class; 13 string Fees; 14 string Marks; 15 string Grade; 16 } 17 public class Staff : Person 18 { 19 string Designation; 20 double Salary; 21 } 22 public class Technical : Staff 23 { 24 string Qualification; 25 string Subject; 26 } 27 public class NonTechnical : Staff 28 { 29 string Dname; 30 string Superior; 31 } 32 }
