剛接觸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 : Animal 16 { 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,Dog Hello,Cat
此時調用的分別是Dog與Cat類中實現的Say方法。
3.都不會影響父類自身的方法。
如:
1 class Program 2 { 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,Dog Hello,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,Cat Hello,Animal
內存原理:
我們都知道,調用對象的方法,實際上是訪問對象方法在內存中的地址。那么既然可以通過c.Say()訪問到父類的方法,說明在對象c中,也有Animal類的Say方法。
事實上,當子類繼承父類的時候,父類中所有方法、字段(包括私有的)都會復制一份放在子類中。而我們所謂的重寫和覆蓋,重寫、覆蓋的是存放在子類中的,復制出來的方法,而不是父類中的方法,所以當然不會對父類產生任何影響。而不能調用私有的、或者被重寫的方法或字段,是由於無權訪問,而不是內存中不存在。
