標籤:main 不同 不能 區別 ima err rri font void
剛接觸C#編程,我也是被override與new搞得暈頭轉向。於是花了點時間翻資料,看部落格,終於算小有領悟,把學習筆記記錄於此。
首先聲明一個父類Animal類,與繼承Animal的兩個子類Dog類與Cat類。父類Animal中有一個Say方法,而子類Dog與Cat分別override(重寫)與new(覆蓋)了Say方法。
讓我們通過執行個體來看一下這兩者有什麼相同與不同之處。
1 public class Animal 2 { 3 public virtual void Say() 4 { 5 Console.WriteLine("Hello,Animal"); 6 } 7 } 8 public class Dog : Animal 9 {10 public override void Say()11 {12 Console.WriteLine("Hello,Dog");13 }14 }15 public class Cat : Animal16 {17 public new void Say()18 {19 Console.WriteLine("Hello,Cat");20 }21 }
首先說override與new的共同點:
- 都是子類在重新實現父類中簽名相同的方法時使用。
- 當聲明一個子類對象時,調用這個方法,調用的都是子類中實現的方法。
例如:
1 class Program 2 { 3 static void Main(string[] arge) 4 { 5 Dog d = new Dog(); 6 Cat c = new Cat(); 7 d.Say();//調用override的方法 8 c.Say();//調用new的方法 9 }10 }
輸出是:
Hello,DogHello,Cat
此時調用的分別是Dog與Cat類中實現的Say方法。
3.都不會影響父類自身的方法。
如:
1 class Program2 {3 static void Main(string[] arge)4 {5 Animal a = new Animal();6 a.Say();//調用父類方法。未受影響。7 }8 }
此時的輸出是:
Hello,Animal
下面說兩者的不同之處:
1.
(1)override:父類方法必須用virtual修飾,表示這個方法是虛方法,可以被重寫。否則不能被重寫。
(2)new : 父類方法不必使用virtual修飾。
2.
(1)override : 使用override時,父類中必須存在簽名完全相同的virtual方法。否則編譯不通過。
如果我在Dog類的Say增加一個string類型的形參,則編譯器會提示:沒有合適的方法可以重寫。
(2)new : 使用new時,父類中最好存在簽名相同的方法。如果沒有,VS會有提示,但編譯不會報錯。此時,new關鍵字便沒有了意義。
如果我在Cat類的Say增加一個string類型的形參,VS會提示:new關鍵字不是必須的。
3.當子類中存在與父類方法簽名相同的方法,而沒有被override或new修飾時,預設為new。
也就是說,override必須寫,而new可以不寫(不推薦)。
4.這是最重要的一點。以上三點都是使用方法的區別,而這一點是兩者在實際使用時效果的區別。
(1)override :重寫後,當子類對象轉換為父類時,無法訪問被重寫的虛方法。也就是,被子類重寫後,虛方法在這個子類中便失效了。
如:
class Program { static void Main(string[] arge) { Dog d = new Dog(); Animal a = d as Animal;//子類轉換為父類。注意此時a與d指向同一對象,但d是作為Dog類訪問,而a是作為Animal類訪問 d.Say();//此時調用的是override的方法 a.Say();//此時調用的也是override的方法 } }
輸出為:
Hello,DogHello,Dog
兩次調用的都是Dog中重寫的Say方法
(2)new : 覆蓋後,當子類對象轉換為父類,可以訪問被覆蓋的父類方法。也就是,轉換為父類後,子類new的方法便失效了,此時調用的是父類方法。
當其再轉換為子類時,調用的又變為子類方法。
如:
1 class Program 2 { 3 static void Main(string[] arge) 4 { 5 Cat c = new Cat(); 6 Animal a = c as Animal;//子類轉換為父類。注意此時a與c指向同一對象,但c是作為Cat類訪問,而a是作為Animal類訪問 7 c.Say();//此時調用的是new的方法 8 a.Say();//此時調用的是父類中的方法 9 }10 }
此時的輸出為:
Hello,CatHello,Animal
記憶體原理:
我們都知道,調用對象的方法,實際上是訪問對象方法在記憶體中的地址。那麼既然可以通過c.Say()訪問到父類的方法,說明在對象c中,也有Animal類的Say方法。
事實上,當子類繼承父類的時候,父類中所有方法、欄位(包括私人的)都會複製一份放在子類中。而我們所謂的重寫和覆蓋,重寫、覆蓋的是存放在子類中的,複製出來的方法,而不是父類中的方法,所以當然不會對父類產生任何影響。而不能調用私人的、或者被重寫的方法或欄位,是由於無權訪問,而不是記憶體中不存在。
C#繼承中的override(重寫)與new(覆蓋)用法