Разница между новым и переопределить

199

Хотите знать, в чем разница между следующим:

Случай 1: Базовый класс

public void DoIt();

Случай 1: Унаследованный класс

public new void DoIt();

Случай 2: Базовый класс

public virtual void DoIt();

Случай 2: Унаследованный класс

public override void DoIt();

Оба случая 1 и 2, по-видимому, имеют одинаковый эффект в зависимости от проведенных тестов. Есть ли разница или предпочтительный способ?

Шираз Бхайджи
источник
2
Дубликат многих вопросов, включая stackoverflow.com/questions/159978/…
Джон Скит,

Ответы:

268

Модификатор переопределения может использоваться в виртуальных методах и должен использоваться в абстрактных методах. Это указывает компилятору на использование последней определенной реализации метода. Даже если метод вызывается по ссылке на базовый класс, он будет использовать реализацию, переопределяющую его.

public class Base
{
    public virtual void DoIt()
    {
    }
}

public class Derived : Base
{
    public override void DoIt()
    {
    }
}

Base b = new Derived();
b.DoIt();                      // Calls Derived.DoIt

позвонит, Derived.DoItесли это отменяет Base.DoIt.

Новый модификатор указывает компилятору использовать реализацию вашего дочернего класса вместо реализации родительского класса. Любой код, который не ссылается на ваш класс, но родительский класс будет использовать реализацию родительского класса.

public class Base
{
    public virtual void DoIt()
    {
    }
}

public class Derived : Base
{
    public new void DoIt()
    {
    }
}

Base b = new Derived();
Derived d = new Derived();

b.DoIt();                      // Calls Base.DoIt
d.DoIt();                      // Calls Derived.DoIt

Сначала позвоню Base.DoIt, потом Derived.DoIt. По сути, это два совершенно отдельных метода, которые имеют одно и то же имя, а не производный метод, переопределяющий базовый метод.

Источник: блог Microsoft

Рахул
источник
5
This indicates for the compiler to use the last defined implementation of a method, как найти последнюю определенную реализацию метода ??
AminM
5
Начните с конкретного класса, проверьте, есть ли в нем реализация интересующего метода. Если это так, все готово. Если это не так, перейдите на один шаг вверх в иерархии наследования, то есть проверьте, есть ли у суперкласса интересующий метод. Продолжайте, пока не найдете интересующий вас метод.
csoltenborn
2
Также обратите внимание, что вы можете только overrideметод, когда базовый класс определяет метод как virtual. Слово virtualявляется базовым классом говоря : «Эй, когда я называю этот метод, он мог бы фактически был заменен производной реализацией, так что я не знаю заранее , какой метод реализации я на самом деле вызова во время выполнения. Так virtualЗначит есть заполнитель для метода. Это подразумевает, что методы, которые не помечены как, virtualне могут быть переопределены. Но вы можете заменить любой не виртуальный метод в производном классе модификатором new, доступным только на производном уровне.
Эрик Бонгерс
177

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

переопределить : переопределяет функциональность виртуального метода в базовом классе, предоставляя другую функциональность.

new : скрывает оригинальный метод (который не должен быть виртуальным), предоставляя другую функциональность. Это следует использовать только там, где это абсолютно необходимо.

Когда вы скрываете метод, вы все равно можете получить доступ к исходному методу путем приведения к базовому классу. Это полезно в некоторых сценариях, но опасно.

Джон Б
источник
2
Почему использование метода, который скрывает базовый метод, опасно? Или вы намекаете, что кастинг вообще опасен?
Марк
3
@Mark - вызывающий может не знать о реализации, вызывая случайное неправильное использование.
Джон Б
Можете ли вы использовать overrideи / или newбез virtualродительского метода?
Аарон Франке
16

В первом случае вы скрываете определение в родительском классе. Это означает, что он будет вызываться только тогда, когда вы имеете дело с объектом как дочерним классом. Если вы приведете класс к его родительскому типу, будет вызван родительский метод. Во втором случае метод переопределяется и будет вызываться независимо от того, был ли объект приведен как дочерний или родительский класс.

tvanfosson
источник
7

попробуйте следующее: (case1)

((BaseClass)(new InheritedClass())).DoIt()

Редактировать: virtual + override разрешается во время выполнения (поэтому переопределение действительно переопределяет виртуальные методы), в то время как new просто создает новый метод с тем же именем и скрывает старый, он разрешается во время компиляции -> ваш компилятор вызовет метод it ' видит

nothrow
источник
3

В случае 1, если вы использовали вызов метода DoIt () унаследованного класса, а тип объявлен как базовый класс, вы увидите действие базового класса даже.

/* Results
Class1
Base1
Class2
Class2
*/
public abstract class Base1
{
    public void DoIt() { Console.WriteLine("Base1"); }
}
public  class Class1 : Base1 
{
    public new void DoIt() { Console.WriteLine("Class1"); }
}
public abstract class Base2
{
    public virtual void DoIt() { Console.WriteLine("Base2"); }
}
public class Class2 : Base2
{
    public override void DoIt() { Console.WriteLine("Class2"); }
}
static void Main(string[] args)
{
    var c1 = new Class1();
    c1.DoIt();
    ((Base1)c1).DoIt();

    var c2 = new Class2();
    c2.DoIt();
    ((Base2)c2).DoIt();
    Console.Read();
}
Мэтью Уайтед
источник
Не могли бы вы опубликовать предупреждение или ошибку, которую вы получаете. Этот код работал нормально, когда я его опубликовал.
Мэтью Уайтед
Все это должно быть вставлено в ваш класс точки входа (Программа). Это было удалено для лучшего форматирования на этом сайте.
Мэтью Уайтед
3

Разница между этими двумя случаями заключается в том, что в случае 1 базовый DoItметод не переопределяется, а просто скрывается. Это означает, что в зависимости от типа переменной зависит, какой метод будет вызван. Например:

BaseClass instance1 = new SubClass();
instance1.DoIt(); // Calls base class DoIt method

SubClass instance2 = new SubClass();
instance2.DoIt(); // Calls sub class DoIt method

Это может привести к путанице и привести к непредвиденному поведению, и его следует избегать, если это возможно. Таким образом, предпочтительным способом будет случай 2.

Шеннон Корниш
источник
3
  • newозначает уважение к вашему типу REFERENCE (слева =), тем самым запустив метод ссылочных типов. Если переопределенный метод не имеет newключевого слова, он ведет себя как есть. Кроме того, это также известно как неполиморфное наследование . То есть «я делаю совершенно новый метод в производном классе, который не имеет абсолютно никакого отношения к каким-либо методам с тем же именем в базовом классе». - сказал Уитакер
  • override, который должен использоваться с virtualключевым словом в его базовом классе, означает уважать ваш тип OBJECT (правая часть =), тем самым запуская метод, переопределяемый независимо от ссылочного типа. Кроме того, это также известно как полиморфное наследование .

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

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

new: если ключевое слово используется методом, в отличие от overrideключевого слова, тип ссылки важен. Если он создан с производным классом, а ссылочный тип является базовым классом, выполняется метод базового класса. Если он создается с производным классом, а ссылочный тип является производным классом, выполняется метод производного класса. А именно, это контраст overrideключевого слова. Если вы забудете или не добавите новое ключевое слово в метод, компилятор будет работать по умолчанию, так как newиспользуется ключевое слово.

class A 
{
    public string Foo() 
    {
        return "A";
    }

    public virtual string Test()
    {
        return "base test";
    }
}

class B: A
{
    public new string Foo() 
    {
        return "B";
    }
}

class C: B 
{
    public string Foo() 
    {
        return "C";
    }

    public override string Test() {
        return "derived test";
    }
}

Звоните в основном:

A AClass = new B();
Console.WriteLine(AClass.Foo());
B BClass = new B();
Console.WriteLine(BClass.Foo());
B BClassWithC = new C();
Console.WriteLine(BClassWithC.Foo());

Console.WriteLine(AClass.Test());
Console.WriteLine(BClassWithC.Test());

Вывод:

A
B
B
base test
derived test

Новый пример кода,

Играйте с кодом, комментируя по одному.

class X
{
    protected internal /*virtual*/ void Method()
    {
        WriteLine("X");
    }
}
class Y : X
{
    protected internal /*override*/ void Method()
    {
        base.Method();
        WriteLine("Y");
    }
}
class Z : Y
{
    protected internal /*override*/ void Method()
    {
        base.Method();
        WriteLine("Z");
    }
}

class Programxyz
{
    private static void Main(string[] args)
    {
        X v = new Z();
        //Y v = new Z();
        //Z v = new Z();
        v.Method();
}
ОСШ
источник
1

Если ключевое слово overrideиспользуется в производном классе, то оно переопределяет родительский метод.

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

саба
источник
1

У меня был тот же вопрос, и это действительно сбивает с толку, вы должны учитывать, что переопределение и новые ключевые слова работают только с объектами базового типа и значения производного класса. Только в этом случае вы увидите эффект переопределения и нового: так что если у вас есть class Aи B, Bнаследует от A, то вы создаете экземпляр объекта следующим образом:

A a = new B();

Теперь при вызове методов будет учитываться его состояние. Переопределить : означает, что он расширяет функцию метода, затем он использует метод в производном классе, тогда как новый сообщает компилятору скрыть метод в производном классе и использовать метод в базовом классе. Вот очень хороший взгляд на эту тему:

https://msdn.microsoft.com/EN-US/library/ms173153%28v=VS.140,d=hv.2%29.aspx?f=255&MSPPError=-2147217396


источник
1

Статья ниже на vb.net, но я думаю, что объяснение новых переопределений очень легко понять.

https://www.codeproject.com/articles/17477/the-dark-shadow-of-overrides

В какой-то момент в статье есть это предложение:

В общем, Shadows предполагает, что вызывается функция, связанная с типом, а Overrides предполагает, что реализация объекта выполнена.

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

Самуил
источник
1

Из всех этих новых - самое запутанное. Посредством экспериментов новое ключевое слово похоже на предоставление разработчикам возможности переопределить реализацию наследуемого класса реализацией базового класса путем явного определения типа. Это как думать иначе.

В приведенном ниже примере результат будет возвращать «Производный результат» до тех пор, пока тип явно не будет определен как тест BaseClass, только тогда будет возвращен «Базовый результат».

class Program
{
    static void Main(string[] args)
    {
        var test = new DerivedClass();
        var result = test.DoSomething();
    }
}

class BaseClass
{
    public virtual string DoSomething()
    {
        return "Base result";
    }
}

class DerivedClass : BaseClass
{
    public new string DoSomething()
    {
        return "Derived result";
    }
}
usefulBee
источник
3
Добавьте свой комментарий, если вы возражаете. Беги так трусливо.
ПолезноБи
0

Функциональная разница не будет показана в следующих тестах:

BaseClass bc = new BaseClass();

bc.DoIt();

DerivedClass dc = new DerivedClass();

dc.ShowIt();

В этом примере Doit, который вызывается, - это тот, которого вы ожидаете вызвать.

Чтобы увидеть разницу, вы должны сделать это:

BaseClass obj = new DerivedClass();

obj.DoIt();

Вы увидите , если вы запустите этот тест , что в случае 1 (как вы определили его), то DoIt()в BaseClassназывается, в случае 2 (как вы определили его), то DoIt()в DerivedClassназывается.

Кен Фальк
источник
-1

В первом случае он вызовет метод производного класса DoIt (), поскольку новое ключевое слово скрывает метод базового класса DoIt ().

Во втором случае он будет вызывать переопределение DoIt ()

  public class A
{
    public virtual void DoIt()
    {
        Console.WriteLine("A::DoIt()");
    }
}

public class B : A
{
    new public void DoIt()
    {
        Console.WriteLine("B::DoIt()");
    }
}

public class C : A
{
    public override void DoIt()
    {
        Console.WriteLine("C::DoIt()");
    }
}

давайте создадим экземпляр этих классов

   A instanceA = new A();

    B instanceB = new B();
    C instanceC = new C();

    instanceA.DoIt(); //A::DoIt()
    instanceB.DoIt(); //B::DoIt()
    instanceC.DoIt(); //B::DoIt()

Все ожидается наверху. Позволяет установить instanceB и instanceC в instanceA, вызвать метод DoIt () и проверить результат.

    instanceA = instanceB;
    instanceA.DoIt(); //A::DoIt() calls DoIt method in class A

    instanceA = instanceC;
    instanceA.DoIt();//C::DoIt() calls DoIt method in class C because it was overriden in class C
cimey
источник
instanceC.DoIt (); даст вам C :: DoIt (), а не B :: DoIt ()
BYS2
-1

введите описание изображения здесь

Все комбинации none, virtual, override, new и abstract:

Виктор Стагуров
источник