Разница между затенением и переопределением в C #?


102

В чем разница между затенением и переопределением метода в C #?

Ответы:


154

Ну наследство ...

предположим, у вас есть эти классы:

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

Предположим, у вас есть базовый класс, и вы используете базовый класс во всем своем коде вместо унаследованных классов, и вы используете shadow, он будет возвращать значения, возвращаемые базовым классом, вместо того, чтобы следовать дереву наследования реального типа объекта.

Запустите код здесь

Надеюсь, я понимаю :)


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

@dribeas, какое определение полиморфизма вы бы использовали, чтобы сделать такой вывод?
AnthonyWJones,

9
@AnthonyWJones, Полиморфизм - это способность одного (производного) типа использоваться в качестве другого (базового) типа, где реальная реализация (поведение) соответствует реальному конкретному (производному) типу. Если вы переопределите, производный метод будет вызываться через ссылку на базу, но если вы
затените его

2
Мне кажется, в вашем примере кода есть ошибка. Приведение должно быть ((A) clB) .Foo (), не так ли?
trendl

1
Нет, потому что бар виртуальный, поэтому он все равно будет называть бар B
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реализация Thing () остается доступной и переопределяемой. Все законно и объяснимо в соответствии с правилами типа .NET, но при этом совершенно неинтуитивно.

Я опубликовал этот ответ, чтобы лучше понять, как все работает, а не как предложение методов, которые можно использовать свободно.


2
Приятно знать, что. Хотя его использование может быть опасным :)
Stormenet

2
Все инструменты могут быть опасны при неправильном использовании.
AnthonyWJones

1
Но удобство использования действительно кажется сомнительным! Есть ли здесь какое-нибудь «серьезное» приложение с открытым исходным кодом?
Jox

4
Юзабилити? Вы имеете в виду полезность? То, что находится на периферии, может показаться бесполезным до тех пор, пока оно вам не понадобится.
AnthonyWJones,

Кажется, что Shadowing появляется ТОЛЬКО, когда играют виртуальные и новые игры. @Stormenet объясняет только переопределение, потому что это нормальное поведение класса - вызывать свой собственный метод, если он не является виртуальным.
Sumit Kapadia

6

Я думаю, что основное отличие состоит в том, что при затенении вы по сути повторно используете имя и просто игнорируете использование суперкласса. При переопределении вы меняете реализацию, но не доступность и подпись (например, типы параметров и возврат). См. Http://www.geekinterview.com/question_details/19331 .


5

Затенение - это концепция VB.NET. В C # затенение называется скрытием. Он скрывает метод производного класса. Это достигается с помощью ключевого слова new.

Ключевое слово Override используется для предоставления полностью новой реализации метода базового класса (помеченного как «Виртуальный») в производном классе.


Ты имеешь в виду. Он скрывает метод производного класса, вместо этого использует метод базового класса. ?
Shaijut

0

В основном, если у вас есть что-то вроде ниже,

Class A
{
}

Class B:A
{
}

A a = new B();

Любой метод, который вы вызываете для объекта 'a', будет выполнен с типом 'a' (здесь тип 'A'). Но если вы реализуете тот же метод в классе B, который уже присутствует в классе A, компилятор будет предупредить вас об использовании ключевого слова «Новое». Если вы используете «Новый», предупреждение исчезнет. Кроме этого, нет никакой разницы между использованием «New» или неиспользованием его в унаследованном классе.

В некоторых ситуациях вам может потребоваться вызвать метод ссылочного класса, который конкретный экземпляр содержит в данный момент, вместо вызова метода для типа объекта. В приведенном выше случае ссылка, которую он содержит, - «B», но тип - «A». Поэтому, если вы хотите, чтобы вызов метода происходил на «B», для этого используйте Virtual и override.

Надеюсь это поможет...

Дэниел Сандип.


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.