Різниця між віртуальною, заміною, новою та герметичною заміною


79

Я досить плутати між деякими поняттями об'єктно - орієнтованого програмування: virtual, override, newіsealed override . Хто-небудь може пояснити відмінності?

Мені цілком ясно, що якщо потрібно використовувати метод похідного класу, можна використовувати overrideключове слово, щоб метод базового класу був замінений похідним класом. Але я не впевнений у new, і sealed override.

Відповіді:


107

Віртуальне ключове слово використовується для зміни методу, властивості, індексатор або подій декларації, і дозволяють йому бути перевизначені в похідному класі. Наприклад, цей метод може бути замінений будь-яким класом, який успадковує його: Використовуйте новий модифікатор, щоб явно приховати член, успадкований від базового класу. Щоб приховати успадкований член, оголосіть його у похідному класі, використовуючи те саме ім'я, та змініть його за допомогою нового модифікатора.

Це все пов’язано з поліморфізмом. Коли для посилання викликається віртуальний метод, фактичний тип об'єкта, на який посилається посилання, використовується для вирішення, яку реалізацію методу використовувати. Коли метод базового класу замінений у похідному класі, використовується версія у похідному класі, навіть якщо викличний код не "знає", що об'єкт є екземпляром похідного класу. Наприклад:

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.

Тепер, якщо ви використовуєте нове ключове слово замість заміни , метод у похідному класі не замінює метод у базовому класі, він просто приховує його. У такому випадку кодуйте так:

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. Вони є фактично двома абсолютно окремими методами, які мають однакову назву, а не похідний метод, який замінює базовий метод.

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

Декларація про переважне властивість може містити запечатаний модифікатор. Використання цього модифікатора перешкоджає похідному класу від подальшого перевизначення властивості. Пристосування до опечатаного майна також опечатані.


дякую за вказівку .. але одна річ мені не спадає на думку ... що таке використання бази b = new Derived ()? Це створення об'єкта базового класу чи похідного класу ??
xorpower

2
Похідний клас. Я думаю, вам доведеться детальніше вивчити питання про поліморфізм. Ось хороший для вашого читання. msdn.microsoft.com/en-us/library/ms173152(v=vs.80).aspx
CharithJ

5
@Xor: У такому випадку ви створюєте екземпляр Derivedоб’єкта і зберігаєте посилання у Baseзмінній. Це дійсно, оскільки Derivedоб'єкт - це Baseтеж об'єкт. Це все одно, що сказати, що нам потрібна "людина", тому ми отримуємо "Джонні", який випадково є людиною. Тут та сама угода.
Джефф Меркадо,

Я хочу додати тут лише один момент. Base b = new Derived()в ньому зазначено, що Baseклас може бути доступний за допомогою Derived classпосилання, оскільки a derived classє спеціалізацією його базового класу. Derivedкласи можуть виконувати всі операції (наприклад, виклик методів базового класу тощо ), які base classможе виконати a . Але a Base classне може виконувати ті операції, які Derived classможе зробити він. Тож Derived d = new Base()не правильно, але Base b = new Derived()правильно.
mmushtaq

Чи можете ви пояснити мету використання newмодифікатора для hide a base class method? У другому прикладі виклик b.SomeOtherMethod()викликає реалізацію базового класу (можна сказати, що він приховав метод похідного класу). Якщо це типовий приклад використання, тоді, newсхоже, використовується, коли абонент має намір мати змінну a compile-time typeдля використання свого методу, а не метод будь-якої, runtime typesяка йому може бути призначена.
Minh Tran

35

Будь-який метод може бути замінений (= 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точки зору, ви телефонуєте Alice.GetName, коли з Mikeтелефону - дзвоните Mike.GetName. Тут не виконується пошук під час виконання - оскільки обидва методи невіртуальні.

Ви завжди можете створити newметоди - віртуальні чи ні, які ви приховуєте.

Це стосується і властивостей, і подій - вони представлені як методи внизу.


1
Немає простої та повної відповіді, ніж ця, яку я десь знайшов. Дякую Локі
Рівз

19

За замовчуванням метод не може бути замінений у похідному класі, якщо він не оголошений virtual, або abstract. virtualозначає перевірку нових реалізацій перед викликом і abstractозначає те саме, але гарантовано буде замінено у всіх похідних класах. Крім того, не потрібна реалізація в базовому класі, оскільки вона буде перевизначена в іншому місці.

Винятком із наведеного є newмодифікатор. Метод не оголошений virtualабо abstractможе бути перевизначений за допомогою newмодифікатора у похідному класі. Коли метод викликається в базовому класі, виконується базовий метод, а коли викликається у похідному класі, виконується новий метод. Усі newключові слова, які ви можете зробити, це мати два методи з однаковим іменем в ієрархії класів.

Нарешті, sealedмодифікатор розриває ланцюжок virtualметодів і робить їх неможливими знову. Це використовується не часто, але варіант є. Це має більше сенсу з ланцюжком із 3 класів, кожен з яких походить від попереднього

A -> B -> C

якщо Aмає virtualабо abstractметод, тобто overriddenin B, то він також може запобігти Cйого зміні знову, оголосивши його sealedв B.

sealedтакож використовується в classes, і саме тут ви часто зустрічаєтесь із цим ключовим словом.

Сподіваюся, це допоможе.


8
 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();
  1. Використання virtualв базовому класі та перевизначення в Derivedдасть D (поліморфізм).
  2. Використання overrideбез virtualin Baseдасть помилку.
  3. Подібним чином написання методу (без заміни) за допомогою virtualнапише 'B' із попередженням (оскільки поліморфізм не проводиться).
  4. Щоб приховати таке попередження, як у наведеному вище пункті, напишіть newперед цим простим методом у Derived.
  5. new Ключове слово - це вже інша історія, воно просто приховує попередження, яке повідомляє, що властивість з тим самим ім’ям знаходиться в базовому класі.
  6. virtualабо newобидва однакові, за винятком нового модифікатора

  7. newі overrideне може бути використаний до того самого методу або властивості.

  8. sealed перед тим, як будь-який клас або метод блокують їх для використання в похідному класі, і це видає помилку часу компіляції.

Вибачте, але -1 через кілька помилок компіляції: метод оголошено кілька разів з однаковими параметрами, без лапок навколо рядків B & D ...
DeveloperDan
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.