Разница между виртуальным, переопределенным, новым и запечатанным переопределением
Я довольно запутался между некоторыми понятиями ООП:virtual
,override
,new
и sealed override
. Кто-нибудь может объяснить разницу?
Я довольно ясно, что если метод производного класса должен использоваться, можно использовать override
ключевое слово, чтобы метод базового класса был переопределен производным классом. Но я не уверен насчет new
и sealed override
.
4 ответа:
The виртуальный ключевое слово используется для изменения метода, свойства, индексатора или объявления события и позволяет переопределить его в производном классе. Например, этот метод может быть переопределен любым производным классом: Используйте новый модификатор, чтобы явно скрыть элемент, унаследованный от базового класса. Чтобы скрыть унаследованный член, объявите его в производном классе с тем же именем и измените его с помощью нового модификатора.
Это все связано с полиморфизмом. Когда виртуальный метод вызывается по ссылке, фактический тип объекта, на который ссылается ссылка, используется для определения того, какую реализацию метода использовать. Когда метод базового класса переопределяется в производном классе, используется версия в производном классе, даже если вызывающий код не "знал", что объект является экземпляром производного класса. Например:
public class Base { public virtual void SomeMethod() { } } public class Derived : Base { public override void SomeMethod() { } } ... Base d = new Derived(); d.SomeMethod();
в конечном итоге вызов производного.SomeMethod, если это переопределяет базу.Метода someMethod.
теперь, если вы используйте новая ключевое слово вместо переопределить метод в производном классе не переопределить метод в базовом классе, он просто скрывает это. В этом случае код выглядит так:
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();
сначала позвонить.Какой-то другой метод , потом производный.SomeOtherMethod . Это фактически два совершенно разных метода, которые имеют одно и то же имя, а не производный метод, переопределяющий базовый метод.
Если вы не укажете ни new, ни переопределяет, результирующий вывод такой же, как если бы вы указали new, но вы также получите предупреждение компилятора (поскольку вы можете не знать, что вы скрываете метод в методе базового класса, или действительно Вы, возможно, хотели его переопределить и просто забыли включить ключевое слово).
переопределяющее объявление свойства может включать загерметизированная модификатор. Использование этого модификатора предотвращает дальнейшее переопределение свойства производным классом. Доступ в запечатанном собственность также опечатаны.
любой метод может быть переопределяемым (=
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"
когда вызывается невиртуальный метод, тело метода не просматривается-компилятор уже знает фактический метод, который должен быть вызван. В то время как с виртуальными методами компилятор не может быть уверен, какой из них вызвать, и это посмотрел вверх во время выполнения в иерархии классов снизу вверх, начиная с типа экземпляра, на который вызывается метод: for
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
перспективу вы называетеAlice.GetName
, когда изMike
' s-вы звонитеMike.GetName
. Здесь не выполняется поиск во время выполнения, поскольку оба метода не являются виртуальными.вы всегда можете создать
new
методы - независимо от того, являются ли методы, которые вы скрываете, виртуальными или нет.это также относится к свойствам и событиям - они представлены в виде методов внизу.
по умолчанию метод не может быть переопределен в производном классе, если он объявлен
virtual
илиabstract
.virtual
означает проверять наличие новых реализаций перед вызовом иabstract
означает то же самое, но он гарантированно будет переопределен во всех производных классах. Кроме того, реализация не требуется в базовом классе, потому что она будет переопределена в другом месте.исключением из вышесказанного является
new
модификатор. Метод не объявленvirtual
илиabstract
можно переопределить с помощьюnew
модификатор в производном классе. При вызове метода в базовом классе выполняется базовый метод, а при вызове в производном классе выполняется новый метод. Все этоnew
ключевые слова позволяет сделать это двумя способами:С тем же именем в иерархии классов.наконец-то a
sealed
модификатор разрывает цепиvirtual
методы и делает их не переопределяемый снова. Это используется не часто, но вариант быть там. Это имеет больше смысла с цепочкой из 3 классов, каждый из которых происходит от предыдущегоA -> B -> C
если
A
естьvirtual
илиabstract
метод, то естьoverridden
наB
, то он также может предотвратитьC
от изменения его снова, объявив егоsealed
inB
.
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
наBase
выдаст ошибку.- аналогично писать метод (без переопределения) с
virtual
будет писать ' B ' с предупреждением (потому что полиморфизм не делается).- чтобы скрыть такое предупреждение, как в выше точки пишите
new
перед этим простой метод вDerived
.new
ключевое слово-это другая история, оно просто скрывает предупреждение, которое говорит, что свойство с тем же именем находится в базовом классе.
virtual
илиnew
оба одинаковы, за исключением новый модификатор
new
иoverride
нельзя использовать перед тем же методом или свойством.sealed
перед любым классом или методом заблокировать его использоваться в производном классе, и это дает ошибку времени компиляции.