C# - использование ключевых слов virtual + override против new

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

9 ответов


ключевое слово "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, если вы использовали override, он напечатал бы true.

(базовый код взят из Joseph Daigle)

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


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

опять же, взяв код Джозефа Дэйгла,

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


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

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 ключевое слово фактически создает совершенно новый член, который существует только для этого конкретного типа.

например

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.


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

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

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

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

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

вызовет метод NewBar() базового класса, тогда как:

(new SubClass()).NewBar()

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


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


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

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

MSDN и другое


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

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

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

если бы вы хотели использовать версию бара, вы бы использовали override или use .

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