Я хочу побудувати відповідь Ахрата . Для повноти різниця полягає в тому, що ОП очікує, що new
ключове слово у методі похідного класу замінить метод базового класу. Насправді це приховує метод базового класу.
У З #, як згадується ще одна відповідь, традиційний метод переосмислення повинен бути явним; метод базового класу повинен бути позначений як, virtual
а похідний клас повинен конкретноoverride
метод базового класу. Якщо це зроблено, то не має значення, чи розглядається об'єкт як екземпляр базового класу або похідного класу; знайдений і названий похідний метод. Це робиться аналогічно, як у C ++; метод, позначений "віртуальний" або "переосмислити", коли компілюється, вирішується "пізно" (під час виконання) шляхом визначення фактичного типу посиланого об'єкта та переміщення ієрархії об'єктів вниз по дереву від типу змінної до фактичного типу об'єкта, знайти найбільш похідну реалізацію методу, визначеного типом змінної.
Це відрізняється від Java, що дозволяє "неявні переопределення"; наприклад, методи (нестатичні), просто визначення методу тієї самої підпису (ім'я та номер / тип параметрів) призведе до того, що підклас замінить надклас.
Оскільки часто корисно розширювати або змінювати функціональність невіртуального методу, яким ви не керуєте, C # також включає new
контекстне ключове слово. new
Ключове слово «шкура» батьківський метод не перекриває його. Будь-який успадкований метод можна приховати, віртуальний він чи ні; це дозволяє вам, розробнику, користуватися членами, які ви хочете успадкувати від батьків, не обробляючи тих, яких ви не маєте, одночасно дозволяючи представляти той самий "інтерфейс" споживачам вашого коду.
Приховування працює аналогічно переосмисленню з точки зору людини, яка використовує ваш об’єкт на рівні або нижче рівня успадкування, на якому визначено спосіб приховування. З прикладу запитання, кодер, який створює Вчителя та зберігає цю посилання у змінній типу Вчитель, побачить поведінку реалізації ShowInfo () від Teacher, яка приховує цю від Person. Однак хтось, хто працює з вашим об'єктом у колекції записів персоналу (як ви є), побачить поведінку Person впровадження ShowInfo (); оскільки метод вчителя не перекриває його батьківського (що також вимагає, щоб Person.ShowInfo () було віртуальним), код, що працює на рівні особи абстракції, не знайде вчителівської реалізації та не використовуватиме його.
Крім того, не тільки new
ключове слово зробить це явно, C # дозволяє приховати прихований метод; просто визначивши метод з тією ж підписом, що і метод батьківського класу, без override
або new
приховає його (хоча він створить попередження компілятора або скаргу від певних помічників рефакторингу, таких як ReSharper або CodeRush). Це компромісний дизайн дизайнерів C #, придуманий між чіткими перекриттями C ++ та неявними, і незважаючи на те, що Java, і хоч він і елегантний, він не завжди сприймає поведінку, яку ви очікували, якщо ви походите з фонової мови на будь-якій із старих мов.
Ось новий матеріал: Це стає складним, коли ви поєднуєте ці два ключові слова у довгому ланцюжку успадкування. Розглянемо наступне:
class Foo { public virtual void DoFoo() { Console.WriteLine("Foo"); } }
class Bar:Foo { public override sealed void DoFoo() { Console.WriteLine("Bar"); } }
class Baz:Bar { public virtual void DoFoo() { Console.WriteLine("Baz"); } }
class Bai:Baz { public override void DoFoo() { Console.WriteLine("Bai"); } }
class Bat:Bai { public new void DoFoo() { Console.WriteLine("Bat"); } }
class Bak:Bat { }
Foo foo = new Foo();
Bar bar = new Bar();
Baz baz = new Baz();
Bai bai = new Bai();
Bat bat = new Bat();
foo.DoFoo();
bar.DoFoo();
baz.DoFoo();
bai.DoFoo();
bat.DoFoo();
Console.WriteLine("---");
Foo foo2 = bar;
Bar bar2 = baz;
Baz baz2 = bai;
Bai bai2 = bat;
Bat bat2 = new Bak();
foo2.DoFoo();
bar2.DoFoo();
baz2.DoFoo();
bai2.DoFoo();
Console.WriteLine("---");
Foo foo3 = bak;
Bar bar3 = bak;
Baz baz3 = bak;
Bai bai3 = bak;
Bat bat3 = bak;
foo3.DoFoo();
bar3.DoFoo();
baz3.DoFoo();
bai3.DoFoo();
bat3.DoFoo();
Вихід:
Foo
Bar
Baz
Bai
Bat
---
Bar
Bar
Bai
Bai
Bat
---
Bar
Bar
Bai
Bai
Bat
Перший набір з п'яти - все очікуване; оскільки кожен рівень має реалізацію і посилається на об'єкт того ж типу, що і інстанціювався, час виконання вирішує кожен виклик на рівень спадкування, на який посилається тип змінної.
Другий набір з п'яти є результатом присвоєння кожному екземпляру змінної безпосереднього типу батьків. Тепер деякі проблеми в поведінці вилітають; foo2
, який фактично є Bar
акторським складом як Foo
, все ще знайде більш похідний метод фактичного типу об'єкта Bar. bar2
є Baz
, але на відміну від foo2
, тому що Baz явно не переосмислює реалізацію Bar (не може; заборонити sealed
це), вона не бачиться під час виконання під час перегляду "зверху вниз", тому реалізація Bar називається замість цього. Зауважте, що Базу не потрібно використовувати new
ключове слово; ви отримаєте попередження про компілятор, якщо ви пропустите ключове слово, але мається на увазі поведінка в C # - це приховати батьківський метод. baz2
- це Bai
, що переосмислюєBaz
snew
реалізація, тому її поведінка схожа на foo2
s; називається реальна реалізація типу об'єкта в Баї. bai2
це a Bat
, який знову приховує реалізацію свого батьківського Bai
методу, і він поводиться так само, як bar2
навіть якщо реалізація Бая не запечатана, тому теоретично Бат міг би замінити метод замість прихованого методу. Нарешті, bat2
це a Bak
, який не має жодної переважаючої реалізації будь-якого виду і просто використовує його батьківський.
Третій набір із п'яти ілюструє повну поведінку роздільної здатності зверху вниз. Насправді все посилається на екземпляр найбільш похідного класу в ланцюжку, Bak
але роздільна здатність на кожному рівні змінного типу виконується, починаючи з цього рівня ланцюга успадкування і переглядаючи до найбільш похідного явного переопрацювання методу, які є ті , в Bar
, Bai
і Bat
. Метод приховування таким чином "розриває" переважаючий ланцюг спадкування; ви повинні працювати з об'єктом на рівні або нижче рівня успадкування, який приховує метод, щоб використовувати метод приховування. В іншому випадку прихований метод "розкривається" і використовується замість цього.