C # - использование ключевого слова виртуальный + переопределение против нового - PullRequest
199 голосов
/ 02 октября 2008

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

Ответы [ 10 ]

221 голосов
/ 02 октября 2008

Я всегда нахожу такие вещи легче понять с помощью картинок:

Снова, принимая код Джозефа Дейгла,

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 при объявлении ваших классов.

Virtual/Override explanation image

178 голосов
/ 02 октября 2008

Ключевое слово "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» - это когда метод никак не связан с версией базового класса.

41 голосов
/ 02 октября 2008

Вот некоторый код, чтобы понять разницу в поведении виртуальных и не виртуальных методов:

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;
    }
}
19 голосов
/ 02 октября 2008

Ключевое слово 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 голосов
/ 02 октября 2008

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

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

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

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

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

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

(new SubClass()).NewBar()

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

8 голосов
/ 02 октября 2008

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

4 голосов
/ 02 октября 2008

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

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

MSDN и Другие

3 голосов
/ 19 июля 2011
  • new ключевое слово для скрытия. - означает, что вы прячете свой метод во время выполнения. Вывод будет основан на методе базового класса.
  • override для переопределения. - означает, что вы вызываете свой метод производного класса со ссылкой на базовый класс. Вывод будет основан на методе производного класса.
1 голос
/ 06 сентября 2016

Моя версия объяснения основана на использовании свойств , чтобы помочь понять различия.

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 версии свойства, Foo и Bar, и для знать, какой из них использовать, c # "выбирает" свойство для текущего типа.

Если бы вы хотели использовать версию Bar, вы бы использовали переопределение или вместо нее использовали Foo foo.

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

0 голосов
/ 05 декабря 2018

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

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

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

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

Не отмечать метод базового класса virtual в производном классе означает: Этот метод помечен как new (статическое связывание).

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

...