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.
Derived
obiektu i przechowujesz odniesienie wBase
zmiennej. Jest to ważne, ponieważDerived
obiekt jest równieżBase
obiektem. To tak, jakby powiedzieć, że potrzebujemy „osoby”, więc otrzymujemy „Johnny'ego”, który jest osobą. Ta sama oferta tutaj.Base b = new Derived()
stwierdza, żeBase
klasa może być dostępna poprzezDerived class
odniesienie, ponieważderived class
jest specjalizacją swojej klasy bazowej.Derived
klasy mogą wykonywać wszystkie operacje (np. wywoływanie metod klasy bazowej itp. ), którebase class
może wykonać. AleBase class
nie może wykonać operacji, któreDerived class
może zrobić. WięcDerived d = new Base()
nie jest poprawne, aleBase b = new Derived()
jest poprawne.new
modyfikatora dohide a base class method
? W drugim przykładzie wywołanieb.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,new
wydaje się, że jest używany, gdy wywołujący zamierza mieć zmienną a,compile-time type
aby użyć swojej metody, a nie metodę,runtime types
która może być do niej przypisana.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
GetPersonType
jest wywoływana przezFiend
instancję, w rzeczywistościPerson.GetPersonType
nazywa się to:Console.WriteLine(friend.GetPersonType()); // "person"
Kiedy metoda wirtualna
GetName
jest wywoływana przezFriend
instancjęFriend.GetName
, nazywa się to:Console.WriteLine(friend.GetName()); // "Onotole"
Kiedy metoda wirtualna
GetName
jest wywoływana przezPerson
instancję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.GetName
ponieważ wygląda na początekFriend
klasy znajduje go od razu, dlaperson.GetName
klasy 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 override
do 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
Alice
perspektywy, dzwoniszAlice.GetName
, kiedy zMike
- dzwoniszMike.GetName
. Nie jest tu wykonywane żadne wyszukiwanie w czasie wykonywania - ponieważ obie metody nie są wirtualne.Zawsze możesz tworzyć
new
metody - 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.
źródło
Domyślnie metody nie można zastąpić w klasie pochodnej, chyba że zostanie zadeklarowana
virtual
lubabstract
.virtual
oznacza sprawdzanie nowszych implementacji przed wywołaniem iabstract
oznacza 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
new
modyfikator. Metoda, która nie została zadeklarowanavirtual
lubabstract
może zostać ponownie zdefiniowana za pomocąnew
modyfikatora 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
sealed
modyfikator przerywa łańcuchvirtual
metod 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 poprzedniejjeśli
A
ma metodęvirtual
lubabstract
, czyli jestoverridden
wB
, może również zapobiecC
jej ponownej zmianie, deklarując jąsealed
wB
.sealed
jest również używany w programieclasses
i właśnie tam często można spotkać to słowo kluczowe.Mam nadzieję, że to pomoże.
źródło
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();
virtual
w klasie bazowej i zastąpienie wDerived
da D (Polimorfizm).override
bezvirtual
inBase
da błąd.virtual
wypisze „B” z ostrzeżeniem (ponieważ nie jest wykonywany żaden polimorfizm).new
przed tą prostą metodą wDerived
.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.virtual
lubnew
oba są takie same, z wyjątkiem nowego modyfikatoranew
ioverride
nie może być użyte przed tą samą metodą lub właściwością.sealed
przed jakąkolwiek klasą lub metodą zablokuj ją, aby była używana w klasie pochodnej i powoduje błąd czasu kompilacji.źródło