В Виртуальное ключевое слово используется для изменения метода, свойства, индексатор или событий декларации, и позволяют ему быть переопределены в производном классе. Например, этот метод может быть переопределен любым наследующим его классом: используйте модификатор new, чтобы явно скрыть член, унаследованный от базового класса. Чтобы скрыть унаследованный член, объявите его в производном классе с тем же именем и измените его с помощью модификатора new.
Все это связано с полиморфизмом. Когда виртуальный метод вызывается для ссылки, фактический тип объекта, на который ссылается ссылка, используется для решения, какую реализацию метода использовать. Когда метод базового класса переопределяется в производном классе, используется версия в производном классе, даже если вызывающий код не «знал», что объект является экземпляром производного класса. Например:
public class Base
{
public virtual void SomeMethod()
{
}
}
public class Derived : Base
{
public override void SomeMethod()
{
}
}
...
Base d = new Derived();
d.SomeMethod();
в конечном итоге вызовет Derived.SomeMethod, если он переопределяет Base.SomeMethod.
Теперь, если вы используете ключевое слово new вместо переопределения , метод в производном классе не переопределяет метод в базовом классе, а просто скрывает его. В этом случае используйте такой код:
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, результат будет таким же, как если бы вы указали new, но вы также получите предупреждение компилятора (поскольку вы можете не знать, что скрываете метод в базовом классе или вы, возможно, захотели переопределить его, но просто забыли включить ключевое слово).
Объявление переопределяющего свойства может включать модификатор sealed . Использование этого модификатора предотвращает дальнейшее переопределение свойства производным классом. Аксессуары запечатанного свойства также запечатаны.
Derived
объекта и сохраняете ссылку вBase
переменной. Это верно, потому что объект тожеDerived
являетсяBase
объектом. Это все равно что сказать, что нам нужен «человек», поэтому мы получаем «Джонни», который оказывается человеком. То же самое и здесь.Base b = new Derived()
в нем говорится, чтоBase
класс может быть доступен поDerived class
ссылке, потому что этоderived class
специализация его базового класса.Derived
классы могут выполнять все операции (например, вызывать методы базового класса и т. д. ), которыеbase class
может выполнять a . НоBase class
не может выполнять те операции, которые онDerived class
может. ТакDerived d = new Base()
что неправильно, ноBase b = new Derived()
правильно.new
модификатораhide a base class method
? Во втором примере вызовb.SomeOtherMethod()
вызывает реализацию базового класса (можно сказать, что он скрыл метод производного класса). Если это типичный пример использования, то,new
похоже, он используется, когда вызывающий объект намеревается иметь переменную acompile-time type
для использования своего метода, а не метод любого,runtime types
который может быть ему назначен.Любой метод может быть переопределен (=
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");
Когда не виртуальный метод
GetPersonType
вызываетсяFiend
экземпляром, на самом деле онPerson.GetPersonType
вызывается:Console.WriteLine(friend.GetPersonType()); // "person"
Когда виртуальный метод
GetName
вызываетсяFriend
экземпляром, онFriend.GetName
вызывается:Console.WriteLine(friend.GetName()); // "Onotole"
Когда виртуальный метод
GetName
вызываетсяPerson
экземпляром, онPerson.GetName
вызывается:Console.WriteLine(person.GetName()); // "generic name"
Когда вызывается невиртуальный метод, тело метода не просматривается - компилятор уже знает фактический метод, который необходимо вызвать. В то время как с виртуальными методами компилятор не может быть уверен, какой из них вызывать, и он просматривается во время выполнения в иерархии классов сверху вниз, начиная с типа экземпляра, для которого вызывается метод: поскольку
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()); // the new method is called, printing "Alice" Console.WriteLine(((Mike)alice).GetName()); // the method hidden by new is called, printing "Mike"
Когда вы вызываете его с
Alice
«точки зрения s вы называетеAlice.GetName
, когда изMike
» s - вы называетеMike.GetName
. Здесь не выполняется поиск во время выполнения - оба метода не виртуальные.Вы всегда можете создавать
new
методы - независимо от того, являются ли методы, которые вы скрываете, виртуальными или нет.Это также относится к свойствам и событиям - они представлены как методы внизу.
источник
По умолчанию метод не может быть переопределен в производном классе, если он не объявлен
virtual
, илиabstract
.virtual
означает проверку наличия новых реализаций перед вызовом иabstract
означает то же самое, но гарантированно будет переопределено во всех производных классах. Кроме того, в базовом классе не требуется реализации, поскольку он будет повторно определен в другом месте.Исключением из вышесказанного является
new
модификатор. Метод не объявленvirtual
илиabstract
может быть переопределен с помощьюnew
модификатора в производном классе. Когда метод вызывается в базовом классе, выполняется базовый метод, а при вызове в производном классе выполняется новый метод. Всеnew
ключевые слова позволяют вам использовать два метода с одинаковым именем. в иерархии классов.Наконец,
sealed
модификатор прерывает цепочкуvirtual
методов и делает их снова недоступными для переопределения. Это не часто используется, но вариант есть. Это имеет больше смысла с цепочкой из 3 классов, каждый из которых является производным от предыдущего.если
A
естьvirtual
илиabstract
метод, то естьoverridden
вB
, то он также может предотвратитьC
от изменения его снова, объявив егоsealed
вB
.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();
virtual
в базовом классе и переопределение вDerived
даст D (полиморфизм).override
безvirtual
inBase
приведет к ошибке.virtual
с предупреждением напишет «B» (поскольку полиморфизм не выполняется).new
перед этим простым методом вDerived
.new
ключевое слово - это другая история, оно просто скрывает предупреждение о том, что свойство с таким же именем находится в базовом классе.virtual
илиnew
оба одинаковы, кроме нового модификатораnew
иoverride
не может использоваться перед тем же методом или свойством.sealed
перед тем, как какой-либо класс или метод заблокируют его для использования в классе Derived, и это даст ошибку времени компиляции.источник