Різниця між затіненням і переосмисленням у C #?


100

Яка різниця між затіненням та перекриттям методу в C #?

Відповіді:


152

Ну спадок ...

припустимо, у вас є такі класи:

class A {
   public int Foo(){ return 5;}
   public virtual int Bar(){return 5;}
}
class B : A{
   public new int Foo() { return 1;}     //shadow
   public override int Bar() {return 1;} //override
}

то коли ви телефонуєте цьому:

A clA = new A();
B clB = new B();

Console.WriteLine(clA.Foo()); // output 5
Console.WriteLine(clA.Bar()); // output 5
Console.WriteLine(clB.Foo()); // output 1
Console.WriteLine(clB.Bar()); // output 1

//now let's cast B to an A class
Console.WriteLine(((A)clB).Foo()); // output 5 <<<-- shadow
Console.WriteLine(((A)clB).Bar()); // output 1

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

Тут запустіть код

Сподіваюся, я маю сенс :)


16
Переосмислення забезпечує поліморфізм, затінення забезпечує різну реалізацію на тому рівні ієрархії, але не поліморфно.
Девід Родрігес - дрибес

@dribeas, Яке визначення поліморфізму ви б використали, щоб зробити такий висновок?
AnthonyWJones

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

2
Здається, у вашому зразковому коді помилка. Кастинг повинен бути ((A) clB) .oo (), чи не так?
trendl

1
Ні, тому що бар є віртуальним, тому він все ще буде називатися B Bar
Stormenet

32

Тінізація - це фактично VB мова, що ми б називали приховуванням у C #.

Часто приховування (затінення у VB) та переосмислення показано як відповідь Stormenet .

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

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

Однак часто пропускають те, що також можна приховати віртуальний метод.

class A
{
    public virtual void DoStuff() { // original implementation }
}

class B : A
{
    public new void DoStuff() {  //new implementation }
}

B b = new B();
A a = b;

b.DoStuff(); //calls new implementation
a.DoStuff(); //calls original implementation.

Зауважте у наведеному вище прикладі DoStuff стає конкретним і його неможливо перекрити. Однак також можна використовувати як virtualі newключові слова разом.

class A
{
    public virtual void DoStuff() { // original implementation }
}

class B : A
{
    public new virtual void DoStuff() {  //new implementation }
}

class C : B
{
    public override void DoStuff() { //replacement implementation }
}

C c = new C();
B b = c;
A a = b;

c.DoStuff(); //calls replacement implementation
b.DoStuff(); //calls replacement implementation
a.DoStuff(); //calls original implementation.

Зауважимо, що незважаючи на те, що всі методи, що стосуються віртуальності, переосмислення на C не впливає на віртуальний метод на A через те, що використання newв B приховує реалізацію A.

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

Зокрема, ви можете зіткнутися з усілякими проблемами, якщо також зміните модифікатори доступності. Наприклад:-

public class Foo
{
    internal Foo() { }
    protected virtual string Thing() { return "foo"; }
}

public class Bar : Foo
{
 internal new string Thing() { return "bar"; }
}

До зовнішньої спадкоємиці Bar, Fooреалізація «s речі () залишається і перевизначенням під'їзду. Всі юридичні та пояснювані згідно з правилами типу .NET ніколи не зовсім інтуїтивні.

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


2
Приємно це знати. Хоча його використання може бути небезпечним :)
Stormenet

2
Усі інструменти можуть бути небезпечними при неправильному використанні.
AnthonyWJones

1
Але, зручність використання справді здається сумнівною! Чи є якісь «серйозні» програми з відкритим кодом, що використовують це?
Джокс

4
Корисність? Ви маєте на увазі корисність? Що-небудь, що знаходиться на окантовці, може здатися марним аж доти, доки вони вам не потрібні.
AnthonyWJones

Здається, що Shadowing потрапляє в картину ТІЛЬКИ, коли віртуальні та нові грають. @ Stormenet пояснює лише переосмислення, оскільки це нормальна поведінка класу, щоб викликати власний метод, якщо він не є віртуальним.
Суміт Кападія

6

Я думаю, що головна відмінність полягає в тому, що за допомогою затінення ви, по суті, повторно використовуєте ім'я та просто ігноруєте використання суперкласу. З переосмисленням ви змінюєте реалізацію, але не доступність та підпис (наприклад, типи параметрів та повернення). Див. Http://www.geekinterview.com/question_details/19331 .


5

Shadowing - це концепція VB.NET. У C # Shadowing відомий як приховування. Він приховує похідний метод класу. Це здійснюється за допомогою ключового слова "new".

Ключове слово Override використовується для забезпечення абсолютно нової реалізації методу базового класу (який позначений "Virtual") у похідному класі.


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

0

Якщо у вас є щось подібне нижче,

Class A
{
}

Class B:A
{
}

A a = new B();

Будь-який метод, який ви викликаєте на об'єкт 'a', буде зроблений на тип 'a' (тут тип - A), але якщо ви реалізуєте той самий метод у класі B, який вже присутній у класі A, компілятор буде надати вам попередження про використання ключового слова "Нове". Якщо ви використовуєте "Нове", попередження зникне. Крім цього немає різниці між використанням "Нового" або не використанням його у спадковому класі.

У деяких ситуаціях вам може знадобитися викликати метод референтного класу, який утримує конкретний екземпляр у той момент, а не викликати метод типу об'єкта. У наведеному вище випадку посилання, яке він має, - "B", а тип - "A". Отже, якщо ви хочете, щоб виклик методу повинен відбуватися на "B", тоді ви використовуєте Virtual і переосмислюєте це для досягнення.

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

Даніель Сандіп.


0

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

Суть в тому, щоб використовувати його в тому, що і еталон, і об'єкт повинні бути одного типу.

class A
{
    public void Test()
    {
        Console.WriteLine("base");
    }
}

class B : A
{
    public new void Test()
    {
        Console.WriteLine("sub");
    }

    public static void Main(string[] args)
    {
        A a = new A();
        B aa = new B();
        a.Test();
        aa.Test();
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.