繼承
在現實生活中有所謂的“種瓜得瓜、種豆得豆”的說法,在生物學概念上就是“遺傳”的概念,在物件導向中“繼承”就類似於生物學中的遺傳,通過繼承,可以更好的實現程式碼的重用(複用,多次使用的意思)、增加擴充套件性、簡化程式碼等。
下面是現實生活中的引用:
定義與使用繼承
繼承一次最常用的就是通過合法程式(比如遺囑)從別人那裡接受財產或爵位等,有點類似於世襲制。
另外一個含義,是人從父母那裡繼承一些特徵,如果孩子從父母繼承的身高、相貌等特徵。那麼在什麼情況下使用繼承,以及在程式中應該如何定義呢?
首先,繼承一般使用在類之間有一定的層次關係,即一個類已經包含了其他類的一些屬性與方法,比如,僱員類包含了技術員工、客服員工、軟體支援、經理等的屬性和方法,這時,我們把僱員類成為父類,技術員工等類成為子類,子類可以通過繼承機制來使用父類的屬性和方法。
概括來說:繼承需要is a的關係(技術員工 is a 僱員)
在C#中,使用“:”來表示一個類繼承自另外一個類。格式如下:
1 2 3 4 5 6 7 8 9 |
class 技術員工:僱員 { //技術員工的屬性定義 // 技術員工的方法定義 } |
現在需要你為一家卡通影視公司開發商業軟體,其中涉及一個會說話的鴨子(比如唐老鴨),在此之前已經有了,普通鴨子類了。程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
Class Duck { private string name; public string Name { get{return name;} set{name=value;} } public void Quck() { Console.WriteLine(“嘎嘎嘎”); } public void Swimming() { Console.WriteLine(“遊啊遊”); } } |
那麼,現在唐老鴨會說人話,怎麼辦呢?同時還會“嘎嘎嘎”叫,原來的方式是在Duck類裡增加方法
1 2 3 4 5 6 7 |
public void Speak() { Console.WriteLine(“說人話了^.^”); } |
然後在Main()方法中呼叫,使用繼承後的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class TangDuck:Duck { public void Speak() { Console.WriteLine(“說人話了^.^”); } } |
同理可以在Main()中進行類的宣告與呼叫
1 2 3 4 5 6 7 8 9 |
static void Main(string[]args) { TangDuck tanglaoya=newTangDuck(); tanglaoya.Speak(); //可以實現說話功能 } |
我們看到類TangDuck在使用繼承實現Speak時,在類名後跟了個“:”,冒號在此處的含義為“繼承自”的意思。繼承特點如下:
1.有助於程式碼的重用;
2.程式碼維護和書寫都簡單得多;
3.父類的可繼承資料成員和方法可用於子類;
4.子類可以輕易的獲得資料成員和方法;
編碼實踐:試完成作業1
深入理解繼承
3.繼承的單根性
4.繼承自上而下是一種逐層具體化的過程,而自下向上是一種逐層抽象化得過程,這種抽象化關係反應為上下層之間的繼承關係。例如:最高層的動物具有最普遍的特徵,而最底層的人則具有較具體的特徵。
專案1:使用訪問修飾符深入學習繼承
專案背景:實現僱員類對人類的程式碼重用。
解決方案:學習如何利用繼承機制來實現程式碼重用。
任務1:編寫程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
using System; usingSystem.Collections.Generic; usingSystem.Text; namespaceExtends { class Person { //欄位 private stringname; private stringaddress; private stringage; private stringemail; //屬性 public stringName { get{ return name; } set{ name = value; } } protected stringAddress { get{ return address; } set{ address = value; } } public stringAge { get{ return age; } set{ age = value; } } internal stringEmail { get{ return email; } set{ email = value; } } //方法 public voidShowName() { Console.WriteLine("姓名:" + name); } protected voidShowAddress() { Console.WriteLine("地址:" + address); } private voidShowAge() { Console.WriteLine("年齡:" + age); } internal voidShowEmail() { Console.WriteLine("郵件:" + email); } //建構函式 public Person() { name = "活人"; address = "人世間 "; age = "1"; email = "haoren@163.com"; } } class Employee:Person //Employee類繼承自Person類 { //欄位:部門 private floatdepartment; //Employee類的方法 public voidShow() { Console.WriteLine(Name+ Address + Email); } } class Program { static voidMain(string[]args) { EmployeetangJun = new Employee(); tangJun.ShowName(); tangJun.Name = "唐峻 "; tangJun.ShowEmail(); tangJun.Show(); } } } |
任務2:編譯程式:
任務3:執行效果
base和this關鍵字
通過前面的學習,我們知道子類只能繼承父類非private的屬性和方法,系統在建立物件時必須呼叫類的構造方法,那麼在建立子類物件時父類的構造方法是怎樣被呼叫的呢?示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
using System; namespace ExtendsExam { classMyBase { publicMyBase() { Console.WriteLine(“基類物件被建立”); } ~MyBase() { Console.WriteLine(“基類物件被銷燬”); } } class SubClass :MyBase() { public SubClass() { Console.WriteLine(“子類物件被建立”); } ~SubClass() { Console.WriteLine(“子類物件被銷燬”); } } classProgram { static void Main(string[]args) { SubClass sc=new SubClass(); } } } |
執行效果:
基類物件被建立
子類物件被建立
子類物件被銷燬
基類物件被銷燬
通過上述的案例,我們會發現子類自動呼叫了父類的建構函式和解構函式。如果父類有多個建構函式(即父類建構函式過載的情況),子類如何知道呼叫父類的那個建構函式呢?此時,用base關鍵字指定呼叫父類的具體建構函式就比較有用。
base的使用:
n 呼叫父類的屬性和方法
1 2 3 4 5 6 7 8 9 10 11 |
public voidDoWork() { string message = string.Format( " {0}上班了,時間{1}", base.Name, base.Time); } |
n 呼叫父類的建構函式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
publicManager(string name, Genders gender, int age,string position,int allowance) :base(name,gender,age) { //經理類擴充套件的屬性 this.position = position; this.allowance = allowance; } |
案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
usingSystem; usingSystem.Collections.Generic; usingSystem.Text; namespace Test { class Person { protected stringname; public stringName { get{ return name; } } public Person(string_name) { name = _name; } public voidShowName() { Console.WriteLine("name={0}", name); } } //Person類的子類 class Employee:Person { public stringid; public Employee(stringsname,string sid):base(sname) { id = sid; } public voidShow() { base.ShowName(); //利用base呼叫方法 Console.WriteLine("name={0}",base.name); //利用base呼叫父類欄位 Console.WriteLine("id={0}", id); } } class Program { static voidMain(string[]args) { Employeeemp = new Employee("聶亞龍","MS007"); emp.Show(); } } } |
this關鍵字:用來呼叫本類成員
通常也可以隱藏this,MSDN總結如下:
限定被相似的名稱隱藏的成員,例如:
1 2 3 4 5 6 7 8 9 |
public Employee(string name, string alias) { this.name = name; this.alias = alias; } |
將物件作為引數傳遞到其他方法,例如:
this指代物件本身,用於訪問本類的常量、欄位、屬性和方法,而且不管訪問元素師任何訪問級別。另外,靜態成員不是物件的一部分,因此不能在靜態方法中引用this。
抽象類定義及其使用
在理解抽象類和抽象方法之前,我們先來看一下“抽象”是什麼意思,漢語詞典“抽象”含義如下:
1. 將複雜物體的一個或幾個特性抽出去,而只去注意其他特性的行動或過程(主要看是否與系統研究的目標一致)
2. 將幾個有區別的物體的共同性質或特性形象地抽取出來或孤立地進行考慮的行動或過程。
3. 抽象對於將東西分類是必需的。
4. 摘要、提煉,抽象化。
從詞典解釋,我們會發現程式裡的抽象理論實際上是在仿效現實中的抽象理論和方法。
抽象類:好比一篇摘要,摘要中涉及的很多要點都要在文章的各個部分實現,同樣抽象類功能需要子類來實現。
我們在查詢某篇文章時往往先看摘要,摘要可以代表這篇文章,也同樣通過檢視抽象類你可以明晰子類提供的功能,並且抽象類物件也可以代表子類例項行使功能。
抽象類最重要的三個描述:
n 抽象類是子類的一個描述
n 抽象類不能自己例項化,但可以代表子類例項
n 抽象類和介面都是用來實現抽象的。
定義如下:
1 2 3 4 5 6 7 |
訪問修飾符 abstract class 類名 { 抽象類體 } |
抽象類定義示例:
1 2 3 4 5 6 7 8 9 10 11 |
publicabstract class Employee { protected string nid; public abstract int GetSalary(); public abstract int Name{get;set;} } |
抽象類的定義和普通類的定義非常相似,只是在class和類的訪問修飾符中間加了一個abstract關鍵字。抽象類面可以定義抽象方法、抽象屬性等。抽象類只是用來描述功能,所以這些抽象的方法、屬性等不需要去實現它,只寫個空殼就可以了。
抽象類的使用:
抽象類不能自己例項化,需要使用其子類來例項化。那麼抽象例項怎樣才能代表子類例項行使子類功能呢?C#中提供了一種重寫的機制來完成這個功能。重寫關鍵字為override,子類使用該關鍵字來重寫基類的抽象方法、抽象屬性等(注意:凡是子類繼承了抽象類,就必須重寫抽象類裡的抽象方法,否則編譯出錯)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
/*抽象類案例*/ usingSystem; abstractclass BuRuDongWu //抽象類:哺乳動物類 { //成員變數 protected int legs; protected BuRuDongWu(int legs) { this.legs=legs; } //抽象方法 public abstract void eat(); //Console.WriteLine("我有方法體"); //抽象方法做實現了嗎? 抽象方法 體現哺乳動物有一個吃的行為就行了,已經代表了所有子類的吃的行為... //抽象方法沒有方法體(不允許有方法體);可以常規方法 public int getLegs() //常規方法,獲得腿數. { return legs; } } classDog:BuRuDongWu //狗類繼承自抽象類:哺乳動物類 { public Dog(int legs):base(legs) {} //子類必須實現所有的抽象方法,通過重寫. public override void eat() //注意,重寫方法相當於子類實現了父類的抽象方法 { Console.WriteLine("狗吃肉"); } } classCat:BuRuDongWu { public Cat(int legs):base(legs) {} public override void eat() //注意,重寫方法相當於子類實現了父類的抽象方法 { Console.WriteLine("貓吃老鼠"); } } classTest { static void Main(string[]args) { //BuRuDongWu lv=new BuRuDongWu(); 實踐證明抽象類不能被例項化; //抽象類必須被子類繼承實現,才能體現功能; --->更好的體現,繼承特性; Dog underDog=new Dog(4); Cat lanMao=newCat(4); underDog.eat(); lanMao.eat(); Console.WriteLine("狗有{0}條腿,貓有{1}條腿",underDog.getLegs(),lanMao.getLegs()); } } |
為什麼要使用父類物件指向子類物件例項呢?留給大家思考。
介面—概念、理解、本質
在講介面概念之前,首先來讓我們看一下現實生活中都有哪些是介面。
從現實生活中可以知道,介面是一套規範,遵守這個規範就可以實現功能(比如、用U盤來回拷貝東西、用滑鼠操作電腦、用攝像頭和電腦建立連線)。
例如,將電器的“開”、“關”行為單獨定義為一個介面,這樣凡是繼承自這個介面的電器就都有了“開”、“關”的方法。讓電燈、電視、電扇、電冰箱都繼承並實現“開關”介面,這樣,對於所有電器來說都有了自己的開關,並且可以針對本身做相應開關的定義。
介面的定義:
1 2 3 4 5 6 7 |
訪問修飾符 interface 介面名 //名稱通常以I開頭 { 介面內容 } |
- 與抽象類一樣,介面裡面的方法成員也不能有方法體;
- 類可以繼承自介面,繼承之後必須實現裡面的所有方法;
- 介面不能被例項化;
- 類在實現介面裡的方法的時,不需要使用override關鍵字,直接實現即可。
- C#中介面不能包含任何資料成員;
- 介面內的訪問修飾符不需要些,預設為public。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
//介面示例 using System; interface OnOff //開關介面 { void On(); void Off(); }//實現介面的類必須實現介面的所有成員(成員變數、成員方法....); class Light:OnOff //燈 { public void On() //Light類繼承了OnOff介面,在自己的類中對介面中的2個方法來做實現. { Console.WriteLine("電燈亮了"); } public void Off() { Console.WriteLine("電燈關了"); } } class TV:OnOff { public void On() //TV類繼承了OnOff介面,在自己的類中對介面中的2個方法來做實現. { Console.WriteLine("電視亮了"); } public void Off() { Console.WriteLine("電視關了"); } } class Test { static void Main(string[]args) { Light meDeng=new Light(); meDeng.On(); meDeng.Off(); TV meTv=new TV(); meTv.On(); meTv.Off(); } } |
介面的使用基本上和抽象類一樣,有人把介面比做雙節棍中的一節,另外一節就是繼承並實現介面的類,只有兩節聯合起來使用才會發生效力。
另外,在C#中介面是多繼承的,介面之間可以互相繼承與多繼承、普通類或抽象類也可以繼承自介面(注意介面不能繼承自類或抽象類),一個類可以同時繼承自類和多個介面。
u 介面與介面之間
介面與介面之間可以互相繼承,繼承的規則和類一樣,即子介面將獲得父介面的內容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
interface Irun{void run();} //跑動 interface Iswim{void swim();} //游泳 interface Itwo:Ifly,Iswim{} //同時實現跑動與游泳 class TwoTank:Itwo { //自己的兩棲坦克 public void run(){…} public void swim(){…} } |
u 介面、抽象類和類
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
interface IA{void playA{};} interface IB{void playB{};} abstract class TC { public abstract void playC(); } class TD:TC,IA,IB {…} |
面向介面的程式設計:
設計模式的祖師GoF,有句名言:Program to an interface,not an implementation,表示對介面程式設計而不要對實現程式設計,更通俗的說法是對抽象程式設計而不是對具體程式設計。
微軟的FCL(基礎類庫),是微軟的一幫牛人已經定義好了的東西,如果你理解了介面,就會發現FCL類庫中使用到了很多介面技術。比如陣列Array類:
publicabstractclass Array :ICloneable,IList,ICollection,IEnumerable,實現了System.Collections名稱空間下的Ienumerable介面,那麼我們可以用此陣列來運算元組元素:
1 2 3 4 5 6 7 8 9 10 11 |
int []a=new int[]{1,2,3,4,5}; System.Collections.Ienumerator it=a.GetEnumerator(); while(it.MoveNext()) { Console.WriteLine(it.Current.ToString()); } |
抽象類與介面的異同:
小結:
在本章中,我們主要學習了:
u 繼承的定義及其使用
u 什麼是抽象類及重寫
u 介面的定義與使用
英語詞彙:
英文 中文
Base 基礎的,基類
Derived 派生的,繼承的
Point 點
Abstract 抽象的
Virtual 虛的
Animal 動物
Sealed 封裝的
Method 方法
Interface 介面
Declare 宣告
Update 更新
Item 項
Override 過載
Furniture 傢俱
Bookshelf 書櫃
練習專案:
1請編碼實現某軟體公司員工的繼承關係:
僱員(Animal)具有行為:上班、工作、下班
僱員包括:技術員工,客服、銷售
這些員工工作的行為各不相同;但上班、下班的行為是一致的。