Разница между виртуальным, переопределением, новым и закрытым переопределением

Я довольно смущен между некоторыми понятиями ООП: virtual , override , new и sealed override . Может ли кто-нибудь объяснить различия?

Я довольно ясно понимаю, что если метод производного classа должен использоваться, можно использовать ключевое слово override чтобы метод базового classа был переопределен производным classом. Но я не уверен в new и sealed override .

Ключевое слово virtual используется для изменения метода, свойства, индексатора или объявления события и позволяет переопределить его в производном classе. Например, этот метод может быть переопределен любым classом, который наследует его. Используйте новый модификатор, чтобы явно скрыть элемент, унаследованный от базового classа. Чтобы скрыть унаследованный элемент, объявите его в производном classе с использованием того же имени и измените его с помощью нового модификатора.

Это все связано с polymorphismом. Когда виртуальный метод вызывается по ссылке, фактический тип объекта, на который ссылается ссылка, используется для определения того, какую реализацию метода использовать. Когда метод базового classа переопределяется в производном classе, используется версия в производном classе, даже если вызывающий код не «знал», что объект является экземпляром производного classа. Например:

 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.

Теперь, если вы используете новое ключевое слово вместо переопределения , метод в производном classе не переопределяет метод в базовом classе, он просто скрывает его. В этом случае код выглядит следующим образом:

 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. Они фактически представляют собой два совершенно разных метода, которые имеют одно и то же имя, а не производный метод, переопределяющий базовый метод.

Если вы не укажете ни новых, ни переопределений, результирующий вывод будет таким же, как если бы вы указали новый, но вы также получите предупреждение о компиляторе (поскольку вы, возможно, не знаете, что скрываете метод в базовом classе метод, или вы, возможно, захотели переопределить его и просто забыли включить ключевое слово).

Объявление переопределяющего свойства может включать в себя запечатанный модификатор. Использование этого модификатора предотвращает дальнейшее переопределение производного classа. Аксессоры закрытого имущества также запечатываются.

Любой метод может быть переопределяемым (= 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 потому что он не виртуальный.

Давайте создадим два экземпляра этих classов:

 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" 

Когда не виртуальный метод вызывается, тело метода не просматривается – компилятор уже знает фактический метод, который нужно вызвать. В то время как с помощью виртуальных методов компилятор не может быть уверен, какой из них вызывать, и он просматривается во время выполнения в иерархии classов от начала до начала, начиная с типа экземпляра, который вызван методом: для friend.GetName он выглядит стартовым в classе Friend и сразу находит его, для classа person.GetName он начинается с Person и находит его там.

Иногда вы создаете подclass, переопределяете виртуальный метод, и вы не хотите, чтобы в иерархии больше переопределялось – для этого вы используете sealed override (говоря, что вы последний, который переопределяет метод):

 class Mike : Friend { public sealed override String GetName() { return "Mike"; } } 

Но иногда ваш друг Майк решает изменить свой пол и, таким образом, его имя Алисе 🙂 Вы можете либо изменить исходный код, либо вместо этого подclass 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 вы вызываете Mike.GetName . Здесь не выполняется поиск во время выполнения – поскольку оба метода не являются виртуальными.

Вы всегда можете создавать new методы – независимо от того, являются ли методы, которые вы скрываете, виртуальными или нет.

Это также относится к свойствам и событиям – они представлены как методы внизу.

По умолчанию метод не может быть переопределен в производном classе, если он не объявлен virtual или abstract . virtual средства проверяют наличие новых реализаций до того, как вызов и abstract означают то же самое, но он гарантированно будет переопределен во всех производных classах. Кроме того, в базовом classе реализация не требуется, поскольку она будет переопределена в другом месте.

Исключением из вышеперечисленного является new модификатор. Метод, не объявленный virtual или abstract может быть переопределен с new модификатором в производном classе. Когда метод вызывается в базовом classе, выполняется базовый метод, а при вызове в производном classе выполняется новый метод. Все new ключевые слова позволяют вам иметь два метода с одинаковым именем в иерархии classов.

Наконец, sealed модификатор разрушает цепочку virtual методов и делает их не переопределяемыми. Это часто не используется, но опция есть. Это имеет большее значение с цепочкой из 3 classов, каждая из которых вытекает из предыдущей

 A -> B -> C 

если 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 

Теперь ключевые слова касаются polymorphismа

  Base b = new Derived(); 
  1. Использование virtual в базовом classе и переопределение в Derived даст D (polymorphism).
  2. Использование override без virtual в Base даст ошибку.
  3. Подобным образом, написание метода (без переопределения) с virtual будет писать «B» с предупреждением (потому что polymorphism не выполняется).
  4. Чтобы скрыть такое предупреждение, как в предыдущем пункте, напишите new перед этим простым методом в Derived .
  5. new ключевое слово – это еще одна история, она просто скрывает предупреждение о том, что свойство с таким же именем присутствует в базовом classе.
  6. virtual или new оба одинаковы, кроме нового модификатора

  7. new и override нельзя использовать перед тем же методом или свойством.

  8. sealed перед тем, как какой-либо class или метод заблокирует его для использования в classе Derived, и он дает ошибку времени компиляции.
  • Безопасное переопределение виртуальных функций C ++
  • Давайте будем гением компьютера.