Разница между new и override

интересно, какая разница между следующим:

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

public void DoIt();

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

public new void DoIt();

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

public virtual void DoIt();

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

public override void DoIt();

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

13 ответов


модификатор override может использоваться на виртуальные методы и должны использоваться на абстрактный метод. Это указывает на компилятор для использования последнего определенного реализация метода. Хотя метод вызывается по ссылке на базовый класс он будет использовать реализация превалирует над ним.

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


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

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

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

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


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


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

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

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


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

/* 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();
}

разница между этими двумя случаями заключается в том, что в случае 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.


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

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

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

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

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

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


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

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

в какой-то момент в статье, есть такая фраза:

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

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


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

в приведенном ниже примере результат будет возвращать "производный результат", пока тип не будет явно определен как тест 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";
    }
}

функциональная разница не будет отображаться в этих тестах:

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 называется.


у меня был тот же вопрос и это действительно сбивает с толку, вы должны учитывать это переопределить и новая ключевые слова, работающие только с объектами типа base class и value производного класса. В этом случае только вы увидите эффект override и new: Так что если у вас есть 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


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

во втором случае он вызовет overriden 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