Вопрос

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

Это было полезно?

Решение

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

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

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

Другие советы

Мне всегда легче понять подобные вещи с помощью фотографий:

И снова, взяв код Джозефа Дейгла,

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

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

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.

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

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

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

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

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

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

(new SubClass()).NewBar()

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

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

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

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

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top