Любой метод может быть переопределен (= virtual
) или нет. Решение принимает тот, кто определяет метод:
class Person
{
public String GetPersonType()
{
return "person";
}
public virtual String GetName()
{
return "generic name";
}
}
Теперь вы можете переопределить те методы, которые можно переопределить:
class Friend : Person
{
public Friend() : this("generic name") { }
public Friend(String name)
{
this._name = name;
}
public override String GetName()
{
return _name;
}
}
Но вы не можете отменить GetPersonType
метод, потому что он не виртуальный.
Давайте создадим два экземпляра этих классов:
Person person = new Person();
Friend friend = new Friend("Onotole");
Когда не виртуальный метод GetPersonType
вызывается Fiend
экземпляром, на самом деле он Person.GetPersonType
вызывается:
Console.WriteLine(friend.GetPersonType());
Когда виртуальный метод GetName
вызывается Friend
экземпляром, он Friend.GetName
вызывается:
Console.WriteLine(friend.GetName());
Когда виртуальный метод GetName
вызывается Person
экземпляром, он Person.GetName
вызывается:
Console.WriteLine(person.GetName());
Когда вызывается невиртуальный метод, тело метода не просматривается - компилятор уже знает фактический метод, который необходимо вызвать. В то время как с виртуальными методами компилятор не может быть уверен, какой из них вызывать, и он просматривается во время выполнения в иерархии классов сверху вниз, начиная с типа экземпляра, для которого вызывается метод: поскольку friend.GetName
он смотрит, начиная с Friend
класса и сразу находит, для person.GetName
класса он начинается сPerson
и находит его там.
Иногда вы создаете подкласс, переопределяете виртуальный метод, и вам больше не нужно никаких переопределений в иерархии - вы используете sealed override
для этого (говоря, что вы последний, кто переопределяет метод):
class Mike : Friend
{
public sealed override String GetName()
{
return "Mike";
}
}
Но иногда ваш друг Майк решает изменить свой пол и, следовательно, свое имя на Алиса :) Вы можете либо изменить исходный код, либо вместо этого создать подкласс Майка:
class Alice : Mike
{
public new String GetName()
{
return "Alice";
}
}
Здесь вы создаете совершенно другой метод с тем же именем (теперь у вас их два). Какой метод и когда вызывается? Это зависит от того, как вы это называете:
Alice alice = new Alice();
Console.WriteLine(alice.GetName());
Console.WriteLine(((Mike)alice).GetName());
Когда вы вызываете его с Alice
«точки зрения s вы называете Alice.GetName
, когда из Mike
» s - вы называете Mike.GetName
. Здесь не выполняется поиск во время выполнения - оба метода не виртуальные.
Вы всегда можете создавать new
методы - независимо от того, являются ли методы, которые вы скрываете, виртуальными или нет.
Это также относится к свойствам и событиям - они представлены как методы внизу.