Różnica między zastąpieniem wirtualnym, nadpisanym, nowym i zapieczętowanym

79

Jestem bardzo mylić między niektórymi pojęciami OOP: virtual, override, newi sealed override. Czy ktoś może wyjaśnić różnice?

Jestem całkiem jasne, że jeśli ma być używana metoda klasy pochodnej, można użyć overridesłowa kluczowego, aby metoda klasy bazowej została zastąpiona przez klasę pochodną. Ale nie jestem pewien co do newi sealed override.

xorpower
źródło

Odpowiedzi:

107

Wirtualny słów kluczowych służy do modyfikowania oświadczenie metody, właściwości, zdarzenia lub indeksatora, i pozwolić mu być nadpisane w klasie pochodnej. Na przykład ta metoda może zostać zastąpiona przez dowolną klasę, która ją dziedziczy: Użyj nowego modyfikatora, aby jawnie ukryć element członkowski odziedziczony z klasy bazowej. Aby ukryć dziedziczony element członkowski, zadeklaruj go w klasie pochodnej przy użyciu tej samej nazwy i zmodyfikuj go za pomocą nowego modyfikatora.

To wszystko ma związek z polimorfizmem. Gdy metoda wirtualna jest wywoływana na odwołaniu, rzeczywisty typ obiektu, do którego odwołuje się odwołanie, jest używany do podjęcia decyzji, której implementacji metody użyć. Gdy metoda klasy bazowej jest zastępowana w klasie pochodnej, używana jest wersja w klasie pochodnej, nawet jeśli kod wywołujący nie „wiedział”, że obiekt był wystąpieniem klasy pochodnej. Na przykład:

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

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

...

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

zakończy się wywołaniem Derived.SomeMethod, jeśli zastąpi to Base.SomeMethod.

Teraz, jeśli użyjesz słowa kluczowego new zamiast override , metoda w klasie pochodnej nie przesłania metody w klasie bazowej, po prostu ją ukryje. W takim przypadku wprowadź następujący kod:

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();

Najpierw wywoła Base.SomeOtherMethod, a następnie Derived.SomeOtherMethod. W rzeczywistości są to dwie całkowicie oddzielne metody, które mają tę samą nazwę, a nie metoda pochodna przesłaniająca metodę podstawową.

Jeśli nie określisz ani new ani overrides, wynikowe dane wyjściowe będą takie same, jak w przypadku określenia new, ale otrzymasz również ostrzeżenie kompilatora (ponieważ możesz nie być świadomy, że ukrywasz metodę w klasie bazowej a może rzeczywiście chciałeś ją zastąpić i po prostu zapomniałeś dołączyć słowa kluczowego).

Nadpisująca deklaracja właściwości może zawierać zapieczętowany modyfikator. Użycie tego modyfikatora zapobiega dalszemu zastępowaniu właściwości przez klasę pochodną. Dostęp do zamkniętej nieruchomości również są zapieczętowane.

CharithJ
źródło
dzięki za wkład ... ale jedna rzecz nie przychodzi mi do głowy to ... do czego służy Base b = new Derived ()? Czy to tworzy obiekt klasy bazowej czy pochodnej?
xorpower
2
Klasy pochodnej. Myślę, że musisz przyjrzeć się więcej na temat polimorfizmu. Tutaj jest dobry do czytania. msdn.microsoft.com/en-us/library/ms173152(v=vs.80).aspx
CharithJ
5
@Xor: W takim przypadku tworzysz wystąpienie Derivedobiektu i przechowujesz odniesienie w Basezmiennej. Jest to ważne, ponieważ Derivedobiekt jest również Baseobiektem. To tak, jakby powiedzieć, że potrzebujemy „osoby”, więc otrzymujemy „Johnny'ego”, który jest osobą. Ta sama oferta tutaj.
Jeff Mercado
Chcę tutaj dodać tylko jeden punkt. Base b = new Derived()stwierdza, że Baseklasa może być dostępna poprzez Derived classodniesienie, ponieważ derived classjest specjalizacją swojej klasy bazowej. Derivedklasy mogą wykonywać wszystkie operacje (np. wywoływanie metod klasy bazowej itp. ), które base classmoże wykonać. Ale Base classnie może wykonać operacji, które Derived classmoże zrobić. Więc Derived d = new Base()nie jest poprawne, ale Base b = new Derived()jest poprawne.
mmushtaq
Czy możesz wyjaśnić cel użycia newmodyfikatora do hide a base class method? W drugim przykładzie wywołanie b.SomeOtherMethod()wywołuje implementację klasy bazowej (można by powiedzieć, że ukryła metodę klasy pochodnej). Jeśli jest to typowy przykład użycia, newwydaje się, że jest używany, gdy wywołujący zamierza mieć zmienną a, compile-time typeaby użyć swojej metody, a nie metodę, runtime typesktóra może być do niej przypisana.
Minh Tran
35

Każda metoda może być zastąpiona (= virtual) lub nie. Decyzję podejmuje ten, kto określa metodę:

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";
    }
}

Teraz możesz zastąpić te metody, które można zastąpić:

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

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

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

Ale nie możesz zastąpić GetPersonType metody, ponieważ nie jest wirtualna.

Utwórzmy dwie instancje tych klas:

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

Kiedy metoda niewirtualna GetPersonTypejest wywoływana przez Fiendinstancję, w rzeczywistości Person.GetPersonTypenazywa się to:

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

Kiedy metoda wirtualna GetNamejest wywoływana przez Friendinstancję Friend.GetName, nazywa się to:

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

Kiedy metoda wirtualna GetNamejest wywoływana przez Personinstancję Person.GetName, nazywa się to:

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

Gdy wywoływana jest metoda niewirtualna, treść metody nie jest przeszukiwana - kompilator już zna metodę, którą należy wywołać. Podczas gdy w przypadku metod wirtualnych kompilator nie może być pewien, który z nich wywołać, i jest sprawdzany w czasie wykonywania w hierarchii klas od dołu do góry, zaczynając od typu instancji, na której metoda jest wywoływana: friend.GetNameponieważ wygląda na początek Friendklasy znajduje go od razu, dla person.GetNameklasy zaczyna się oPerson i tam ją znajduje.

Czasami tworzysz podklasę, nadpisujesz metodę wirtualną i nie chcesz już więcej przesłonięć w hierarchii - używasz sealed overridedo tego (mówiąc, że jesteś ostatnim, który zastępuje metodę):

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

Ale czasami twój przyjaciel Mike decyduje się zmienić swoją płeć, a tym samym swoje imię na Alice :) Możesz albo zmienić oryginalny kod, albo zamiast tego podklasę Mike:

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

Tutaj tworzysz zupełnie inną metodę o tej samej nazwie (teraz masz dwie). Która metoda i kiedy jest wywoływana? To zależy od tego, jak to nazwiesz:

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"

Kiedy nazywasz to z Aliceperspektywy, dzwonisz Alice.GetName, kiedy z Mike- dzwonisz Mike.GetName. Nie jest tu wykonywane żadne wyszukiwanie w czasie wykonywania - ponieważ obie metody nie są wirtualne.

Zawsze możesz tworzyć newmetody - niezależnie od tego, czy ukrywane metody są wirtualne, czy nie.

Dotyczy to również właściwości i zdarzeń - są one przedstawiane jako metody poniżej.

Loki Kriasus
źródło
1
Nie ma prostej i kompletnej odpowiedzi niż ta, którą nigdzie znalazłem. Dzięki Loki
Reevs
19

Domyślnie metody nie można zastąpić w klasie pochodnej, chyba że zostanie zadeklarowana virtuallub abstract. virtualoznacza sprawdzanie nowszych implementacji przed wywołaniem i abstractoznacza to samo, ale gwarantuje się, że zostanie zastąpiony we wszystkich klasach pochodnych. Ponadto nie jest potrzebna żadna implementacja w klasie bazowej, ponieważ zostanie ona ponownie zdefiniowana w innym miejscu.

Wyjątkiem od powyższego jest newmodyfikator. Metoda, która nie została zadeklarowana virtuallub abstractmoże zostać ponownie zdefiniowana za pomocą newmodyfikatora w klasie pochodnej. Gdy metoda jest wywoływana w klasie bazowej, wykonywana jest metoda bazowa, a po wywołaniu w klasie pochodnej nowa metoda jest wykonywana. Wszystkienew pozwalają słowa kluczowe, to mieć dwie metody o tej samej nazwie w hierarchii klas.

Wreszcie sealedmodyfikator przerywa łańcuch virtualmetod i sprawia, że ​​nie można ich ponownie zastąpić. Nie jest to często używane, ale istnieje opcja. Ma to większy sens w przypadku łańcucha 3 klas, z których każda pochodzi od poprzedniej

A -> B -> C

jeśli Ama metodę virtuallub abstract, czyli jest overriddenw B, może również zapobiec Cjej ponownej zmianie, deklarując ją sealedw B.

sealedjest również używany w programie classesi właśnie tam często można spotkać to słowo kluczowe.

Mam nadzieję, że to pomoże.

John Alexiou
źródło
8
 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");
  }
}

Teraz pierwsza rzecz pierwsza

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

Teraz słowa kluczowe dotyczą polimorfizmu

 Base b = new Derived();
  1. Użycie virtualw klasie bazowej i zastąpienie w Derivedda D (Polimorfizm).
  2. Korzystanie overridebez virtualin Baseda błąd.
  3. Podobnie napisanie metody (bez nadpisywania) z virtualwypisze „B” z ostrzeżeniem (ponieważ nie jest wykonywany żaden polimorfizm).
  4. Aby ukryć takie ostrzeżenie jak w powyższym punkcie, napisz newprzed tą prostą metodą w Derived.
  5. new słowo kluczowe to inna historia, po prostu ukrywa ostrzeżenie, które mówi, że właściwość o tej samej nazwie znajduje się w klasie bazowej.
  6. virtuallub newoba są takie same, z wyjątkiem nowego modyfikatora

  7. newi overridenie może być użyte przed tą samą metodą lub właściwością.

  8. sealed przed jakąkolwiek klasą lub metodą zablokuj ją, aby była używana w klasie pochodnej i powoduje błąd czasu kompilacji.
Charlie
źródło
Niestety, ale -1 ponieważ wielu błędów kompilacji: Metoda ogłosił kilka razy z tymi samymi parametrami, bez cudzysłowów wokół struny B & D ...
DeveloperDan