C # - використання ключового слова віртуальне + переосмислення проти нового


204

Які відмінності між оголошенням методу в базовому типі " virtual" та його переосмисленням у дочірньому типі з використанням overrideключового слова " " на відміну від простого використання newключового слова під час оголошення методу відповідності для дочірнього типу?


3
MSDN кажуть: "Використання newстворює нового учасника з тим самим іменем і змушує приховати початковий член, тоді як overrideрозширює реалізацію для спадкового члена"
AminM


Відповіді:


181

Ключове слово "новий" не переосмислює, воно означає новий метод, який не має нічого спільного з методом базового класу.

public class Foo
{
     public bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public new bool DoSomething() { return true; }
}

public class Test
{
    public static void Main ()
    {
        Foo test = new Bar ();
        Console.WriteLine (test.DoSomething ());
    }
}

Це надрукує помилкове значення, якщо ви використали переопределення, воно надрукувало б true.

(Базовий код, взятий у Джозефа Дайга)

Тож, якщо ви робите справжній поліморфізм, ви ВЖЕ ЗАВЖДИМИ НАЗАД . Єдине місце, де потрібно використовувати "new" - це коли метод жодним чином не пов'язаний з версією базового класу.


Ви повинні переглянути свій код, я зробив методи статичними (що справедливо для використання "нового" ключового слова). Але я вирішив, що зрозуміліше використовувати методи екземплярів.
Джозеф Дайгл

1
Дякую, я пропустив "статичну" частину. Слід приділити більше уваги майбутньому
Альбертейн

1
Зауважте, що рядок Foo test = new Bar () тут є вирішальним, нове ключове слово / метод переопределення методів визначає, який метод викликається, коли ви ставите Bar у змінну Foo.
Thomas N

... тож це точно так само, як просто не мати virtualв основі та overrideу похідному? Чому воно існує? Код все ще буде працювати без ш / о new- так це суто просто читабельність?
Дон Чідл

Ваша відповідь, шановний пане, досить розпливчаста. нові та віртуальні переохолодження роблять майже те саме, лише різниця полягає в тому, що новий HIDES метод у батьківському класі та переосмислює .. ну, це перекриває його. Звичайно, він друкує помилково, оскільки тестовий об’єкт типу Foo, якщо він буде типу Bar, він надрукував би true. Дуже хитрий приклад, хоча.
MrSilent

228

Я завжди вважаю, що подібні речі легше зрозуміти за допомогою малюнків:

Знову, взявши код Джозефа Дайґла,

public class Foo
{
     public /*virtual*/ bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public /*override or new*/ bool DoSomething() { return true; }
}

Якщо ви зателефонуєте таким чином код:

Foo a = new Bar();
a.DoSomething();

ПРИМІТКА. Важливим є те, що наш об’єкт насправді є Bar, але ми зберігаємо його у змінній типуFoo (це схоже на його кастинг)

Тоді результат буде таким, залежно від того, чи використовували ви virtual/ overrideабо newдекларували свої класи.

Віртуальне / переосмислене зображення пояснення


3
Дякую .... але ви можете, будь ласка, пояснити трохи про картинку вище стосовно кастингу, який ви сказали?
Одісех

На жаль, якщо забув додати ці кілька рядків до моєї попередньої коментар: ви маєте на увазі, що віртуальні / переважаючі та невітові / нові використовуються лише для концепції поліморфізму, і коли ви просто оголошуєте змінну (не використовуючи кастинг), вони не означають? Знову дякую.
odiseh

ця відповідь саме те, що я шукав. Єдиною проблемою у мене було те, що зображення на
мерехтінні

7
Справжні зусилля, щоб відповісти на питання.
iMatoria

Я б сказав, якщо ви можете виправити зображення? Її заблоковано за брандмауером корпусу ...
Джеремі Томпсон

43

Ось декілька кодів, щоб зрозуміти різницю в поведінці віртуальних та невіртуальних методів:

class A
{
    public void foo()
    {
        Console.WriteLine("A::foo()");
    }
    public virtual void bar()
    {
        Console.WriteLine("A::bar()");
    }
}

class B : A
{
    public new void foo()
    {
        Console.WriteLine("B::foo()");
    }
    public override void bar()
    {
        Console.WriteLine("B::bar()");
    }
}

class Program
{
    static int Main(string[] args)
    {
        B b = new B();
        A a = b;
        a.foo(); // Prints A::foo
        b.foo(); // Prints B::foo
        a.bar(); // Prints B::bar
        b.bar(); // Prints B::bar
        return 0;
    }
}

дякую за це - але навіщо використовувати new"приховування" базового методу, коли просто не використовується переопределення, схоже, те саме?
Дон Чедл,

1
Я не думаю, що він був створений для того, щоб не допустити перевищення базового класу. Я думаю, що він був створений, щоб уникнути зіткнення імені, оскільки ви не можете перемогти метод, якого немає, virtualі компілятор поскаржиться, якщо він побачить те саме ім'я функції в класі " virtual
кров'яна

19

newКлючове слово на насправді створює абсолютно новий елемент , який існує тільки на цьому типі конкретного.

Наприклад

public class Foo
{
     public bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public new bool DoSomething() { return true; }
}

Метод існує на обох типах. Якщо ви використовуєте рефлексію та отримуєте членів типу Bar, ви дійсно знайдете два методи, які називаються DoSomething()абсолютно однаково. Використовуючи, newви ефективно приховуєте реалізацію в базовому класі, так що, коли класи виходять з Bar(на моєму прикладі), виклик методу base.DoSomething()переходить до, Barа не Foo.


9

virtual / override повідомляє компілятору, що два способи пов'язані між собою і що за певних обставин, коли ви думаєте, що ви викликаєте перший (віртуальний) метод, насправді правильно називати другий (замінений) метод. Це основа поліморфізму.

(new SubClass() as BaseClass).VirtualFoo()

Викличе метод перекладеного VirtualFoo () SubClass.

new повідомляє компілятору, що ви додаєте метод до похідного класу з тим же ім'ям, що і метод у базовому класі, але вони не мають відношення один до одного.

(new SubClass() as BaseClass).NewBar()

Викличе метод NewBar () BaseClass, тоді як:

(new SubClass()).NewBar()

Викличе метод NewBar () SubClass.


дуже подобається це речення "розповідає укладачеві"
Міна Габріель

"фундамент поліморфізму" - це ще одна стильна приказка :)
Джеремі Томпсон

8

Крім технічних деталей, я думаю, що використання віртуального / переосмислення передає багато семантичної інформації про дизайн. Коли ви оголошуєте метод віртуальним, ви вказуєте, що очікуєте, що класи реалізації можуть захотіти надати власні, не за замовчуванням реалізації. Опускаючи це в базовому класі, також виражає сподівання, що методу за замовчуванням повинно вистачити для всіх класів-реалізаторів. Аналогічно, можна використовувати абстрактні декларації, щоб змусити класи-виконавці забезпечити власну реалізацію. Знову ж таки, я думаю, що це дуже багато повідомляє про те, як програміст очікує використання коду. Якби я писав як базовий, так і впроваджуючий клас і виявив, що використовую новий, я б серйозно переосмислив рішення не робити метод віртуальним у батьківському і заявляю про свої наміри конкретно.


Технічні пояснення нового та переосмислення є надійними, але ця відповідь, мабуть, допомагає найбільше для орієнтації на розробники, на яких слід використовувати.
Суллі

4

Різниця між ключовим словом переопределення та новим ключовим словом полягає в тому, що перший метод переосмислює метод, а пізній метод приховує.

Перегляньте наступні посилання для отримання додаткової інформації ...

MSDN і інші


3
  • newключове слово призначено для приховування. - означає, що ви ховаєте свій метод під час виконання. Результат буде заснований на методі базового класу.
  • overrideза переоцінку. - означає, що ви посилаєтесь на похідний метод класу із посиланням на базовий клас. Вихід буде заснований на похідному методі класу.

1

Моя версія пояснення походить від використання властивостей, щоб допомогти зрозуміти відмінності.

overrideдосить просто, правда? Основний тип переосмислює батьківський.

newце, мабуть, оману (для мене це було). З властивостями простіше зрозуміти:

public class Foo
{
    public bool GetSomething => false;
}

public class Bar : Foo
{
    public new bool GetSomething => true;
}

public static void Main(string[] args)
{
    Foo foo = new Bar();
    Console.WriteLine(foo.GetSomething);

    Bar bar = new Bar();
    Console.WriteLine(bar.GetSomething);
}

Використовуючи налагоджувач, ви можете помітити, що він Foo fooмає 2 GetSomething властивості, оскільки він насправді має 2 версії властивості, Foos і Bar's, і щоб знати, яку саме використовувати, c # "вибирає" властивість для поточного типу.

Якби ви хотіли скористатися версією Bar, ви б Foo fooзамість цього використали переопределення або використання .

Bar barмає лише 1 , оскільки хоче абсолютно нової поведінки GetSomething.


0

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

Позначення методу за virtualдопомогою засобів: Пов'яжіть цей метод, використовуючи тип часу виконання об'єкта, а не тип часу компіляції (динамічне прив'язування).

Позначення virtualметоду базового класу за допомогою overrideпохідного класу означає: Цей метод повинен бути пов'язаний, використовуючи тип часу виконання об'єкта (динамічне прив'язування).

Позначення virtualметоду базового класу за newпохідним класом означає: Це новий метод, який не має відношення до того ж з тим самим іменем у базовому класі, і його слід пов'язати, використовуючи тип часу компіляції об'єкта (статичне прив'язування).

Не маркування virtualметоду базового класу у похідному класі означає: Цей метод позначений як new(статичне зв'язування).

Позначення методу abstractозначає: Цей метод є віртуальним, але я не оголошу для нього тіло, а його клас також абстрактний (динамічне прив'язування).

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