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


204

В чем различия между объявлением метода в базовом типе " virtual" и последующим переопределением его в дочернем типе с помощью overrideключевого слова " " по сравнению с простым использованием newключевого слова " " при объявлении метода сопоставления в дочернем типе?


3
В MSDN говорится: «Использование newсоздает новый элемент с тем же именем и приводит к тому, что исходный элемент становится скрытым, в то же время overrideрасширяется реализация для унаследованного элемента»
AminM

посмотреть этот stackoverflow.com/questions/17717570/…

Ответы:


181

Ключевое слово «new» не переопределяет, оно означает новый метод, который не имеет ничего общего с методом базового класса.

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 ());
    }
}

Это печатает false, если вы использовали переопределение, это напечатало бы true.

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

Так что, если вы делаете настоящий полиморфизм, вы ДОЛЖНЫ ВСЕГДА ПЕРЕПИСАТЬ . Единственное место, где вам нужно использовать «new», - это когда метод никак не связан с версией базового класса.


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

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

1
Обратите внимание, что здесь важна строка Foo test = new Bar (), ключевое слово new / override для методов определяет, какой метод вызывается, когда вы помещаете Bar в переменную Foo.
Томас N

... так это точно так же, как просто не имея virtualв основе и overrideв производной? Почему это существует? Код по-прежнему будет работать даже без кода new- так это просто читабельность?
Дон Чидл

Ваш ответ, дорогой сэр, довольно расплывчато. new и virtual-override делают одно и то же, единственное отличие состоит в том, что new HIDES вызывает метод в родительском классе и переопределяет ... что ж, переопределяет его. Конечно, он печатает false, потому что ваш тестовый объект имеет тип 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

Упс Если забыл добавить эти несколько строчек в мой пред. комментарий: вы имеете в виду, что виртуальные / переопределенные и не витальные / новые используются только для концепции полиморфизма, а когда вы просто объявляете переменную (не используя приведение), они не имеют в виду? Еще раз спасибо.
odiseh

этот ответ именно то, что я искал. Единственная проблема, с которой я столкнулся, заключается в том, что на моем рабочем месте заблокированы изображения flickr, поэтому мне пришлось обращаться к нему через телефон ...
mezoid

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и компилятор будет жаловаться, если он увидит то же имя функции в классе «bloodline» без virtualподписи
mr5

19

newКлючевое слово на самом деле создает совершенно новый элемент , который существует только на этом типе конкретного.

Например

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

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

Метод существует на обоих типах. Когда вы используете отражение и получаете члены типа Bar, вы на самом деле найдете 2 вызываемых метода, DoSomething()которые выглядят абсолютно одинаково. Используя его, newвы эффективно скрываете реализацию в базовом классе, так что когда классы являются производными Bar(в моем примере), вызов метода base.DoSomething()идет Barи не выполняется Foo.


9

virtual / override сообщает компилятору, что оба метода связаны, и что в некоторых случаях, когда вы думаете, что вызываете первый (виртуальный) метод, на самом деле правильно вместо этого вызывать второй (переопределенный) метод. Это основа полиморфизма.

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

Вызовет переопределенный метод VirtualFoo () подкласса.

new сообщает компилятору, что вы добавляете метод в производный класс с тем же именем, что и метод в базовом классе, но они не имеют никакого отношения друг к другу.

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

Вызовет метод NewBar () BaseClass, тогда как:

(new SubClass()).NewBar()

Вызовет метод NewBar () подкласса.


очень нравится это предложение "говорит компилятору"
Мина Габриэль

«основание полиморфизма» - еще одна классная поговорка :)
Джереми Томпсон,

8

Помимо технических деталей, я думаю, что использование virtual / override передает много семантической информации о дизайне. Когда вы объявляете метод виртуальным, вы указываете, что ожидаете, что реализующие классы могут захотеть предоставить свои собственные реализации, отличные от заданных по умолчанию. Опуская это в базовом классе, также объявляем ожидание, что метод по умолчанию должен быть достаточным для всех реализующих классов. Точно так же можно использовать абстрактные объявления, чтобы заставить реализующие классы предоставлять свою собственную реализацию. Опять же, я думаю, что это многое говорит о том, как программист ожидает, что код будет использоваться. Если бы я писал как базовый, так и реализующий классы и обнаружил, что использую новый, я бы серьезно переосмыслил решение не делать метод виртуальным в родительском и специально объявил о своем намерении.


Технические объяснения переопределения new vs являются разумными, но этот ответ, похоже, больше всего помогает руководителям разработчиков по использованию.
Салли

4

Разница между ключевым словом override и новым ключевым словом заключается в том, что первый выполняет переопределение метода, а второй - скрытие метода.

Проверьте следующие ссылки для получения дополнительной информации ...

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вместо нее override или use .

Bar barимеет только 1 , так как он хочет полностью нового поведения для GetSomething.


0

Не помечать метод чем-либо означает: Привязать этот метод, используя тип компиляции объекта, а не тип времени выполнения (статическое связывание).

Маркировка метода с virtual помощью средства: привязать этот метод, используя тип времени выполнения объекта, а не тип времени компиляции (динамическое связывание).

Маркировка virtualметода базового класса overrideв производном классе означает: это метод, который должен быть связан с использованием типа времени выполнения объекта (динамическое связывание).

Маркировка virtualметода базового класса с помощью newв производном классе означает: Это новый метод, который не имеет отношения к методу с тем же именем в базовом классе, и его следует связать с использованием типа времени компиляции объекта (статическая привязка).

Отсутствие пометки virtualметода базового класса в производном классе означает: этот метод помечен как new(статическая привязка).

Маркировка метода abstractозначает: этот метод является виртуальным, но я не буду объявлять тело для него, а его класс также является абстрактным (динамическое связывание).

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.