2009-09-09 106 views
134

想知道有什麼區別以下之間:差異覆蓋

案例1:基地班

public void DoIt(); 

案例1:繼承類

public new void DoIt(); 

案例2:基類

public virtual void DoIt(); 

案例2:繼承類

public override void DoIt(); 

根據我運行的測試,案例1和案例2似乎都具有相同的效果。有沒有區別,或者一個首選的方式?

+2

的許多問題,包括http://stackoverflow.com/questions/159978/c-keyword-usage-virtualoverride-vs-new – 2009-09-09 11:46:18

回答

170

override修飾符可在 虛擬方法一起使用,並且必須在 抽象方法使用。這表示 編譯器使用最後定義的 方法的實現。即使 該方法被稱爲 的基準類,它將使用 實現覆蓋它。

public class Base 
{ 
    public virtual void DoIt() 
    { 
    } 
} 

public class Derived : Base 
{ 
    public override void DoIt() 
    { 
    } 
} 

Base b = new Derived(); 
b.DoIt();      // Calls Derived.DoIt 

將調用Derived.DoIt如果重寫Base.DoIt

new修飾符指示 編譯器使用你的子類實現 而不是父類 實現。任何代碼不是 引用您的類,但父類 類將使用父類 的實現。

public class Base 
{ 
    public virtual void DoIt() 
    { 
    } 
} 

public class Derived : Base 
{ 
    public new void DoIt() 
    { 
    } 
} 

Base b = new Derived(); 
Derived d = new Derived(); 

b.DoIt();      // Calls Base.DoIt 
d.DoIt();      // Calls Derived.DoIt 

會先調用Base.DoIt,然後Derived.DoIt。它們實際上是兩個完全分開的方法,它們碰巧具有相同的名稱,而不是派生方法重寫基本方法。

來源:Microsoft blog

+4

重複'這指示編譯器使用最後一個實現定義的method'。怎麼能找到最後定義的一個方法的實現? – AminM 2014-03-05 19:30:53

+5

從具體類開始,檢查它是否具有感興趣的方法的實現。如果是這樣,你就完成了。如果不是,則在繼承層次結構中向上一步,即檢查超類是否具有感興趣的方法。繼續,直到找到感興趣的方法。 – csoltenborn 2014-10-24 08:16:30

7

嘗試以下幾點:(情形)

((BaseClass)(new InheritedClass())).DoIt() 

編輯:虛擬+覆蓋在運行時(因此忽略真正覆蓋虛擬方法)解決,而新的剛創造新的方法具有相同的名稱,並隱藏了舊,它在編譯時解析 - >你的編譯器會調用它看到的方法

13

在第一種情況下,你將定義隱藏在父類中。這意味着只有當您將對象作爲子類處理時纔會調用它。如果您將該類轉換爲其父類型,則將調用該父類的方法。在第二種情況下,該方法被覆蓋並且將被調用,而不管該對象是否作爲子類或父類被強制轉換。

149

虛擬:表示的方法可以通過繼承

倍率被覆蓋:覆蓋的虛擬方法的功能在鹼類,提供不同的功能。

隱藏原始方法(它不必是虛擬的),提供不同的功能。這應該只在絕對必要的地方使用。

當您隱藏某個方法時,仍然可以通過向上投射到基類來訪問原始方法。這在某些情況下很有用,但很危險。

+0

爲什麼向上施放隱藏基方法的方法是危險的?或者你是否暗示一般來說鑄造是危險的? – Mark 2016-11-07 02:39:05

+1

@Mark - 調用者可能不知道該實現,導致意外誤用。 – 2016-11-07 16:26:06

3

兩種情況之間的區別在於情況1中,基地DoIt方法不會被覆蓋,只是隱藏。這意味着取決於變量的類型取決於調用哪個方法。例如:

BaseClass instance1 = new SubClass(); 
instance1.DoIt(); // Calls base class DoIt method 

SubClass instance2 = new SubClass(); 
instance2.DoIt(); // Calls sub class DoIt method 

這可能會令人困惑,並導致非預期的行爲,應儘可能避免。因此,首選的方法將是案例2.

3

如果您使用的情況1調用繼承類的DoIt()方法,而類型聲明爲基類,則甚至會看到基類的操作。

/* Results 
Class1 
Base1 
Class2 
Class2 
*/ 
public abstract class Base1 
{ 
    public void DoIt() { Console.WriteLine("Base1"); } 
} 
public class Class1 : Base1 
{ 
    public new void DoIt() { Console.WriteLine("Class1"); } 
} 
public abstract class Base2 
{ 
    public virtual void DoIt() { Console.WriteLine("Base2"); } 
} 
public class Class2 : Base2 
{ 
    public override void DoIt() { Console.WriteLine("Class2"); } 
} 
static void Main(string[] args) 
{ 
    var c1 = new Class1(); 
    c1.DoIt(); 
    ((Base1)c1).DoIt(); 

    var c2 = new Class2(); 
    c2.DoIt(); 
    ((Base2)c2).DoIt(); 
    Console.Read(); 
} 
+0

當你嘗試你的代碼時,你會將它顯示爲無效標記 – Learner 2013-02-13 10:54:26

+0

你能發佈你收到的警告或錯誤嗎?當我最初發布它時,此代碼工作正常。 – 2013-02-13 12:55:38

+0

這應該全部粘貼在您的入門級(程序)中。這被刪除,以允許在這個網站上更好的格式。 – 2013-02-13 12:57:44

1

如果在派生類中使用關鍵字override,那麼它將覆蓋父方法。

如果關鍵字new用於派生類,則派生方法由父方法隱藏。

0

功能相差不會顯示在這些測試:

BaseClass bc = new BaseClass(); 

bc.DoIt(); 

DerivedClass dc = new DerivedClass(); 

dc.ShowIt(); 

在這種exmample,被稱爲是你期望被稱爲一個大一。

爲了看你有沒有做到這一點的區別:

BaseClass obj = new DerivedClass(); 

obj.DoIt(); 

你會看到,如果您運行測試的情況下,1(因爲你定義它),在BaseClassDoIt()被調用時,在情況2中(如你所定義的),調用的DerivedClass

0

我有同樣的問題,這真的令人困惑, 你應該考慮覆蓋關鍵字類型的基類和派生類的價值的物品唯一的工作。在這種情況下,只有你會看到倍率和新的作用: 所以,如果你有class AB,從AB繼承,那麼你實例化一個對象是這樣的:

A a = new B(); 

現在上調用方法將採取其狀態考慮在內。 覆蓋:意味着它擴展了方法的功能,然後它使用派生類中的方法,而新的告訴編譯器隱藏派生類中的方法並改用基類中的方法。 這是一個非常良好的視線這一主題:

https://msdn.microsoft.com/EN-US/library/ms173153%28v=VS.140,d=hv.2%29.aspx?f=255&MSPPError=-2147217396

1

我的方式記住這兩個關鍵字,它們彼此相反的承擔。

overridevirtual關鍵字必須定義爲覆蓋該方法。使用override關鍵字的方法,無論引用類型(基類或派生類的引用)是否用基類實例化,都會運行基類的方法。否則,派生類的方法運行。

new:如果關鍵字被方法使用,與override關鍵字不同,則引用類型很重要。如果它用派生類實例化,並且引用類型是基類,則運行基類的方法。如果它使用派生類實例化,並且引用類型是派生類,則將運行派生類的方法。也就是說,這是override關鍵字的對比。順便說一句,如果您忘記或省略向該方法添加新關鍵字,編譯器將默認使用new關鍵字。

class A 
{ 
    public string Foo() 
    { 
     return "A"; 
    } 

    public virtual string Test() 
    { 
     return "base test"; 
    } 
} 

class B: A 
{ 
    public new string Foo() 
    { 
     return "B"; 
    } 
} 

class C: B 
{ 
    public string Foo() 
    { 
     return "C"; 
    } 

    public override string Test() { 
     return "derived test"; 
    } 
} 

呼叫主:

A AClass = new B(); 
Console.WriteLine(AClass.Foo()); 
B BClass = new B(); 
Console.WriteLine(BClass.Foo()); 
B BClassWithC = new C(); 
Console.WriteLine(BClassWithC.Foo()); 

Console.WriteLine(AClass.Test()); 
Console.WriteLine(BClassWithC.Test()); 

輸出:

A 
B 
B 
base test 
derived test 
1

下面的文章是在vb.net,但我認爲關於新VS覆蓋的解釋是很容易掌握。

https://www.codeproject.com/articles/17477/the-dark-shadow-of-overrides

在文章中的某個時刻,有這樣一句話:

在一般情況下,陰影假定與該類型相關聯的功能 調用,而覆蓋假定目標的實現是 執行。

這個問題的接受答案是完美的,但我認爲這篇文章提供了很好的例子來爲這兩個關鍵詞之間的差異添加更好的含義。

-1

其中,是最容易混淆的。通過實驗,new關鍵字就像給開發人員提供了通過顯式定義類型來覆蓋基類實現的繼承類實現的選項。這就像是在想另一種方式。

在下面的示例中,結果將返回「派生結果」,直到類型明確定義爲BaseClass測試,然後才返回「基礎結果」。

class Program 
{ 
    static void Main(string[] args) 
    { 
     var test = new DerivedClass(); 
     var result = test.DoSomething(); 
    } 
} 

class BaseClass 
{ 
    public virtual string DoSomething() 
    { 
     return "Base result"; 
    } 
} 

class DerivedClass : BaseClass 
{ 
    public new string DoSomething() 
    { 
     return "Derived result"; 
    } 
} 
+0

如果您反對,請添加您的評論。撞擊和跑步是如此懦弱。 – usefulBee 2017-08-22 16:03:09

0

在第一種情況下,它將調用派生類DoIt()方法,因爲new關鍵字隱藏了基類DoIt()方法。

在()

public class A 
{ 
    public virtual void DoIt() 
    { 
     Console.WriteLine("A::DoIt()"); 
    } 
} 

public class B : A 
{ 
    new public void DoIt() 
    { 
     Console.WriteLine("B::DoIt()"); 
    } 
} 

public class C : A 
{ 
    public override void DoIt() 
    { 
     Console.WriteLine("C::DoIt()"); 
    } 
} 

,它會調用重寫DOIT第二種情況讓創建這些類

A instanceA = new A(); 

    B instanceB = new B(); 
    C instanceC = new C(); 

    instanceA.DoIt(); //A::DoIt() 
    instanceB.DoIt(); //B::DoIt() 
    instanceC.DoIt(); //B::DoIt() 

一切以高於預期的實例。將instanceB和instanceC設置爲instanceA並調用DoIt()方法並檢查結果。

instanceA = instanceB; 
    instanceA.DoIt(); //A::DoIt() calls DoIt method in class A 

    instanceA = instanceC; 
    instanceA.DoIt();//C::DoIt() calls DoIt method in class C because it was overriden in class C