Każda metoda może być zastąpiona (= virtual
) lub nie. Decyzję podejmuje ten, kto określa metodę:
class Person
{
public String GetPersonType()
{
return "person";
}
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;
}
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 przez Fiend
instancję, w rzeczywistości Person.GetPersonType
nazywa się to:
Console.WriteLine(friend.GetPersonType());
Kiedy metoda wirtualna GetName
jest wywoływana przez Friend
instancję Friend.GetName
, nazywa się to:
Console.WriteLine(friend.GetName());
Kiedy metoda wirtualna GetName
jest wywoływana przez Person
instancję Person.GetName
, nazywa się to:
Console.WriteLine(person.GetName());
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ątek Friend
klasy znajduje go od razu, dla person.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());
Console.WriteLine(((Mike)alice).GetName());
Kiedy nazywasz to z Alice
perspektywy, 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ć 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.