簡體   English   中英

virtual、override、new和sealed override的區別

[英]The difference between virtual, override, new and sealed override

我對 OOP 的一些概念感到很困惑: virtualoverridenew和 seal sealed override 誰能解釋這些差異?

我很清楚,如果要使用派生的 class 方法,可以使用override關鍵字,這樣基本的 class 方法將被派生的 class 覆蓋。 但我不確定newsealed override

virtual關鍵字用於修改方法、屬性、索引器或事件聲明,並允許在派生的 class 中覆蓋它。 例如,此方法可以被任何繼承它的 class 覆蓋:使用 new 修飾符顯式隱藏從基 class 繼承的成員。 要隱藏繼承的成員,請在派生的 class 中使用相同的名稱聲明它,並使用 new 修飾符對其進行修改。

這都與多態性有關。 當在引用上調用虛擬方法時,引用所引用的 object 的實際類型用於決定使用哪個方法實現。 When a method of a base class is overridden in a derived class, the version in the derived class is used, even if the calling code didn't "know" that the object was an instance of the derived class. 例如:

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

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

...

Base d = new Derived();
d.SomeMethod();

如果覆蓋 Base.SomeMethod,最終將調用 Derived.SomeMethod。

現在,如果您使用new關鍵字而不是override ,則派生 class 中的方法不會覆蓋基本 class 中的方法,它只是隱藏它。 在這種情況下,代碼如下:

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

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

...


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

將首先調用 Base.SomeOtherMethod,然后是 Derived.SomeOtherMethod。 它們實際上是兩個完全獨立的方法,它們恰好具有相同的名稱,而不是覆蓋基本方法的派生方法。

如果您未指定 new 或 overrides,則生成的 output 與您指定 new 時相同,但您也會收到編譯器警告(因為您可能不知道您在基礎 class 中隱藏了一個方法方法,或者實際上您可能想要覆蓋它,只是忘記包含關鍵字)。

覆蓋的屬性聲明可能包含sealed修飾符。 使用此修飾符可防止派生的 class 進一步覆蓋該屬性。 密封屬性的訪問者也被密封。

任何方法都可以是可覆蓋的(= virtual )或不可覆蓋。 決定由定義方法的人做出:

class Person
{
    // this one is not overridable (not virtual)
    public String GetPersonType()
    {
        return "person";
    }

    // this one is overridable (virtual)
    public virtual String GetName()
    {
        return "generic name";
    }
}

現在您可以覆蓋那些可覆蓋的方法:

class Friend : Person
{
    public Friend() : this("generic name") { }

    public Friend(String name)
    {
        this._name = name;
    }

    // override Person.GetName:
    public override String GetName()
    {
        return _name;
    }
}

但是您不能覆蓋GetPersonType方法,因為它不是虛擬的。

讓我們創建這些類的兩個實例:

Person person = new Person();
Friend friend = new Friend("Onotole");

Fiend實例調用非虛擬方法GetPersonType時,實際上調用的是Person.GetPersonType

Console.WriteLine(friend.GetPersonType()); // "person"

Friend實例調用虛擬方法GetName時,調用的是Friend.GetName

Console.WriteLine(friend.GetName()); // "Onotole"

Person實例調用虛擬方法GetName時,調用的是Person.GetName

Console.WriteLine(person.GetName()); // "generic name"

當調用非虛擬方法時,不會查找方法體 - 編譯器已經知道需要調用的實際方法。 而虛擬方法編譯器無法確定調用哪一個,並且在運行時在 class 層次結構中從下到上從調用該方法的實例類型開始查找:對於friend.GetName ,它看起來開始在Friend class 並立即找到它,對於person.GetName class 它從Person開始並在那里找到它。

有時您創建一個子類,覆蓋一個虛擬方法並且您不希望在層次結構中再有任何覆蓋 - 為此您使用sealed override (假設您是最后一個覆蓋該方法的人):

class Mike : Friend
{
    public sealed override String GetName()
    {
        return "Mike";
    }
}

但有時你的朋友 Mike 決定改變他的性別,因此把他的名字改成 Alice :) 你可以改變原始代碼,或者改為子類 Mike:

class Alice : Mike
{
    public new String GetName()
    {
        return "Alice";
    }
}

在這里,您創建了一個完全不同的具有相同名稱的方法(現在您有兩個)。 調用哪種方法以及何時調用? 這取決於你如何稱呼它:

Alice alice = new Alice();
Console.WriteLine(alice.GetName());             // the new method is called, printing "Alice"
Console.WriteLine(((Mike)alice).GetName());     // the method hidden by new is called, printing "Mike"

當您從Alice的角度調用它時,您調用Alice.GetName ,當從Mike的角度調用時,您調用Mike.GetName 這里沒有進行運行時查找 - 因為這兩種方法都是非虛擬的。

您始終可以創建new方法 - 無論您隱藏的方法是否為虛擬方法。

這也適用於屬性和事件——它們在下面表示為方法。

默認情況下,不能在派生的 class 中覆蓋方法,除非它被聲明為virtualabstract virtual表示在調用之前檢查較新的實現abstract表示相同,但保證在所有派生類中被覆蓋。 此外,在基本 class 中不需要實現,因為它將在其他地方重新定義。

上述情況的例外是new修飾符。 可以在派生的 class 中使用new修飾符重新定義未聲明為virtualabstract的方法。 當在基礎 class 中調用該方法時,將執行基礎方法,而在派生 class 中調用該方法時,將執行新方法。 所有new關鍵字允許您在 class 層次結構中使用兩個具有相同名稱的方法。

最后一個sealed的修飾符打破了virtual方法鏈,使它們不再被覆蓋。 這不經常使用,但選項在那里。 使用 3 個類的鏈更有意義,每個類都派生自前一個類

A -> B -> C

如果A有一個在B中被overriddenvirtualabstract方法,那么它還可以通過在B中聲明它sealed防止C再次更改它。

sealed也用在classes中,這就是你經常會遇到這個關鍵字的地方。

我希望這有幫助。

 public class Base 
 {
   public virtual void SomeMethod()
   {
     Console.WriteLine("B");
   }
  }

public class Derived : Base
{
   //Same method is written 3 times with different keywords to explain different behaviors. 


   //This one is Simple method
  public void SomeMethod()
  {
     Console.WriteLine("D");
  }

  //This method has 'new' keyword
  public new void SomeMethod()
  {
     Console.WriteLine("D");
  }

  //This method has 'override' keyword
  public override void SomeMethod()
  {
     Console.WriteLine("D");
  }
}

現在第一件事

 Base b=new Base();
 Derived d=new Derived();
 b.SomeMethod(); //will always write B
 d.SomeMethod(); //will always write D

現在關鍵字都是關於多態性的

 Base b = new Derived();
  1. 在基礎 class 中使用virtual並在Derived中覆蓋將給出 D(多態性)。
  2. Base中使用沒有virtualoverride會出錯。
  3. 同樣,使用virtual編寫方法(無覆蓋)將寫入帶有警告的“B”(因為沒有完成多態性)。
  4. 要隱藏上述警告,請在Derived中的那個簡單方法之前編寫new
  5. new關鍵字是另一回事,它只是隱藏了警告,告訴您在基礎 class 中存在同名屬性。
  6. virtualnew兩者都相同,除了new 修飾符

  7. newoverride不能在相同的方法或屬性之前使用。

  8. 在任何 class 之前sealed或方法將其鎖定以用於派生 class 並給出編譯時錯誤。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM