前言:
繼承這點事,說多不多,說少不少,這里只描述了一些我認為的基礎篇,望各位大神指教。
本節參照了C#高級編程和Think in java對繼承的描述,我個人認為OOP只是思想,故看明白一個就說通的,只是語法上的區別。
- 權限限制關鍵字
- 類繼承
- 接口繼承
1.類繼承
類繼承基本寫法:
public class SuperClass : Object{} public class SubClass : SuperClass{}
我們用":"來表示類的繼承, 而且每個類只能有一個父類(這個靠譜啊,你不可能有兩個親身父親,C++不論)。
SuperClass繼承了Object類, Object類是頂級類,你自定義的頂級類(這里說SuperClass)會自動繼承這個類(我看有很多人說:你的每個類都會繼承Object,我們說過,你只能有一個親生父親,這樣的說法不是很嚴謹,這樣的Tier1, Tier2, Tier3的層次下來,你的高層可以直接用Object所開放的一些方法)。
類繼承的好處:
我們先來看些該死的溫柔(類)!!!

public class Dog { public string Name { get; set; } public string Age { get; set; } public void Run() { Console.WriteLine("Dog is running."); } } public class Cat { public string Name { get; set; } public string Age { get; set; } public void Run() { Console.WriteLine("Cat is running."); } }
粗看一下,沒什么問題,貓類和狗類 都有名字,年齡和一個跑的方法。 (當年,我寫完還點頭,雙手叉腰,嗯,很好,不錯--傻的一塌糊塗,冏)
但是如果那天有個變態叫你把所有動物都寫一邊,你開始造輪子了,1234567.....,終於要完工了, 大喜過望,那個變態回來告訴你,不好意思,弄錯了,年齡沒有意義,可以去掉(納尼....). 這個時候你就需要再去1234567....,oh Fuck...難道我不能只改一次嗎?
完 全 可 以->繼承

public abstract class Animal { public string Name { get; set; } public string Age { get; set; } public void Run() { Console.WriteLine(string.Format("{0} is running.", Name)); } } public class Dog : Animal { } public class Cat : Animal { }
代碼所示之處我們提取一個父類:Aniaml(抽象類..過后再說這個).我們重新來完成那個變態的需求,我們只要開辟一個類然后繼承一下Animal就好了,
(場景恢復)嘿,年齡沒有意義,可以去掉(簡單....),我們只要把Animal中的年齡去掉,就OK了,一步到位。
到這里看出來繼承的好處沒:消除你重復的代碼,讓修改更加的方便。
繼承中的Virtual(虛方法)
這里又有人提出另一個問題了,如果我發現我父類的方法不能滿足我的條件了,但是我又不想重載,我能重寫嗎?答案:完全可以。
C#提供一個Virtual的一個標識,可以用於方法重寫(java都說虛方法)

public abstract class Animal { public string Name { get; set; } public string Age { get; set; } public virtual void Run() { Console.WriteLine(string.Format("{0} is running.", Name)); } } public class Dog : Animal { public override void Run() { Console.WriteLine("Dog is running."); } } public class Cat : Animal { }
看Animal中的Run方法加上了virtual標識這個方法說虛方法,也就說可以被重寫,在Dog類中,我們使用了override標識我們重寫了Run的方法,這樣我們在實例化對象Cat的時候,我調用Run方法就是我們重寫過的。
有人提出疑問了:如果不寫virtual 和 override,直接在Cat中重寫Run方法,實例化Cat cat=new Cat()后調用Run方法也是一樣的結果。 回答:C#不像JAVA都說虛方法,如果不想寫virtual 和 override的話,CLR認為這個是隱藏,所以要在子類的Run方法上添加一個new關鍵字,如果不添加會出現
warning。
public class Cat:Animal
{
public new void Run()
{
Console.WriteLine("....");
}
}
New 關鍵字代表我要隱藏父類同名,同參數的方法(這里有幾個可以討論的地方,在多態那章會提出來說)
抽象方法
為什么會出現抽象方法,OOP告訴我們,實例化就像我們現實中創造一件東西,就如上例來看,我們如何去創造一個叫Animal的一個實體,這樣是不是很奇怪,所以我們出現了抽象類。 抽象類無法被實例化,只能被繼承使用。public abstract class Animal,在class前面定義abstract關鍵字就好了。
抽象類可以定義抽象的方法,用abstract關鍵字來標識,所有繼承該抽象類的子類必須實現這個抽象方法,來一個實例:

public abstract class Animal { public string Name { get; set; } public string Age { get; set; } public virtual void Run() { Console.WriteLine(string.Format("{0} is running.", Name)); } public abstract void Scream(); } public class Dog : Animal { public override void Run() { Console.WriteLine("Dog is running."); } public override void Scream() { throw new NotImplementedException(); } } public class Cat : Animal { public override void Scream() { throw new NotImplementedException(); } }
在Animal的抽象類中(我們說了為什么把Animal定義成抽象類),定義了一個Scream()的抽象方法,我們看見了。抽象方法是沒有實現體的,直接后面跟上";"作為結束。在子類Dog和Cat類里,我們實現了Scream這個方法(不可以不實現,只要你用IDE工具,編譯時會報錯),我們在來看子類的Scream方法有override的關鍵字,所以abstract被認為是虛方法,用法類似。
注:這里還沒有說接口,抽象類和接口一起用非常的常見,等說到接口,再詳細的來說下抽象類的其他作用。
類繼承的壞處
所謂有得必有失,繼承給我們帶來了很多好的地方,消除代碼重復,讓層次更加清晰,修改方便等好處,但是隨之帶來的壞處也是很明顯的,當我們繼承的層次變多了,整個的架構會變得十分臃腫而導致不清晰,拿個簡單的例子來說,假設我們說從猩猩進化而來,那尾巴是否已經沒有了,而猩猩還是有的,這就會導致使用的迷糊,我們做類的時候要把該類的東西放在此類中,做父類的時候把對應的公共的東西提出來,而一直用繼承的話很恐怖,所以這里又引出一個新的概念->組合。
組合概念就說在類中放一個其他類的對象,如果要用就賦值,不要用了就直接拿掉(父親我們總不能放棄吧)。
執行循序

public abstract class SuperClass { public static string StaticField = "StaticFieldInSuperClass"; private string _privateField = "_privateFieldInSuperClass"; public string publicField = "publicFieldInSuperClass"; public SuperClass() { } } public class SubClass : SuperClass { public static string StaticFieldInDog = "StaticFieldInSubClass"; private string _privateField = "_privateFieldInSubClass"; public SubClass() { } }
我們看一下這兩個類SuperClass里面有靜態字段,私有字段,共有字段和一個構造,SubClass有一個靜態字段,私有字段。
我們實例化SubClass subClass=new SubClass()的時候,執行循序如下:
1.先初始化子類(subclass)的字段。
2.執行子類構造,這時會發現它擁有父類,所以暫停初始化子類構造,而初始化父類(SuperClass)
3.初始化父類(SuperClass)的字段
4.執行父類構造
5.返回子類繼續初始化子類構造
這里有興趣的朋友可以直接debug看步驟就明白了。
不可繼承的類
擁有關鍵字sealed類都不能被繼承。
public sealed class ConfigHelper { }
故可以認為里面所有的字段和方法都說自帶sealed,因為無法繼承就無法重寫。
2.接口繼承
關於前言Class的抽象類,我們說到抽象類可以實現抽象方法,在Animal中有一個Run的抽象方法,但是我們人也能Run, 而不能去繼承Animal,因為我們的關系圖應該在People或者Human下比較靠譜。 我們這時就可以用接口來做了。
我的概念中的接口:
1.是一個規則,制定程序員只能做什么的規則
2.只關注行為,不關注字段的一個神奇的產物
3.用來做底層抽象和設計框架的不二神器

public interface IRun { void Run(); } public abstract class Animal : IRun { public abstract void Run(); } public class Dog : Animal { public override void Run() { throw new NotImplementedException(); } } public abstract class People : IRun { public abstract void Run(); } public class Man : People { public override void Run() { throw new NotImplementedException(); } }
我們來看一下上面的代碼,People和Animal都說抽抽象類,我們並沒有實現IRun接口下的Run方法,而是直接做成了抽象方法(這里可以證明,接口里面的方法都說抽象方法),而是由我們的子類Man和Dog分別去實現了,這樣可以說明,接口關注的只是行為,因為IRun接口可以被所有可以跑的物體去實現。
關於第一個點:我強烈建議不要在類中任意的去加public方法,因為我建議只要有接口,就用接口作為對象,這樣的好處有幾點:
1.所有實現了該特定接口的類看起來都這樣
2.接口被用來建立類與類之間的協議。(同步開發的時候,只關注接口就行了)
3.定義了規則,無論你底層的編寫人員怎么加public方法你是看不見的,因為作為高層我只認接口里面的方法(這里要明白多態)。
4.接口可以跨類繼承體系來使用(就像IRun run=new Dog(), IRun run=new Man() 這樣)
對於這篇來說是我對接口淺顯的理解,肯定不全,我其實覺得接口在框架級上的可以說的很多,但說這個技術,我真心覺得沒什么好說的。下面是我對接口一些功能的總結:
1.接口 能多繼承接口,類可以多繼承實現。
2.接口里能包含:事件、屬性、索引器、靜態方法、靜態字段、靜態構造函數以及常量。
3.出來抽象類以外,所有實現接口的類都要實現其定義的方法