С# - использование ключевого слова virtual + override vs. new

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

Ответ 1

Ключевое слово "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.

(Базовый код, сделанный Джозефом Дайгле)

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

Ответ 2

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

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

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

Ответ 3

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

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

Ответ 4

Ключевое слово 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.

Ответ 5

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

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

Вызовите метод SubClass переопределить метод VirtualFoo().

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

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

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

(new SubClass()).NewBar()

Вызов метода SubClass NewBar().

Ответ 6

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

Ответ 7

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

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

MSDN и Другое

Ответ 8

  • new ключевое слово для скрытия. - означает, что вы скрываете свой метод во время выполнения. Выход будет основан на методе базового класса.
  • override для переопределения. - означает, что вы вызываете метод производного класса с ссылкой базового класса. Вывод будет основан на методе производного класса.

Ответ 9

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

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

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

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

Ответ 10

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

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

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

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

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

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