Разница между новыми и отменой
Интересно, какая разница между следующими:
Случай 1: базовый класс
public void DoIt();
Случай 1: Унаследованный класс
public new void DoIt();
Случай 2: базовый класс
public virtual void DoIt();
Случай 2: Унаследованный класс
public override void DoIt();
Оба случая 1 и 2, похоже, имеют тот же эффект, основанный на тестах, которые я выполнил. Есть ли разница или предпочтительный способ?
Ответы
Ответ 1
Модификатор переопределения может использоваться виртуальных методов и должны использоваться на абстрактные методы. Это указывает на компилятор использовать последние определенные реализация метода. Даже если метод называется ссылкой на базовый класс будет использовать реализация превалирует над ним.
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
Ответ 2
virtual: указывает, что метод может быть переопределен наследником
переопределить: переопределяет функциональность виртуального метода в базовом классе, предоставляя различные функции.
новый: скрывает исходный метод (который не обязательно должен быть виртуальным), предоставляя различные функции. Это следует использовать только там, где это абсолютно необходимо.
Когда вы скрываете метод, вы все равно можете получить доступ к исходному методу, добавив листинг в базовый класс. Это полезно в некоторых сценариях, но опасно.
Ответ 3
В первом случае вы скрываете определение в родительском классе. Это означает, что он будет вызываться только в том случае, если вы имеете дело с объектом как дочерний класс. Если вы примените класс к его родительскому типу, будет вызван родительский метод. Во втором случае метод переопределяется и будет вызываться независимо от того, был ли объект включен в качестве дочернего или родительского класса.
Ответ 4
попробуйте следующее: (case1)
((BaseClass)(new InheritedClass())).DoIt()
Edit: virtual + override разрешены во время выполнения (поэтому переопределение действительно отменяет виртуальные методы), в то время как новые просто создают новый метод с тем же именем и скрывают старый, он разрешен во время компиляции → ваш компилятор вызовет метод "видит"
Ответ 5
Разница между двумя случаями заключается в том, что в случае 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.
Ответ 6
В случае 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();
}
Ответ 7
Мой способ запомнить оба ключевых слова, чтобы они были противоположны друг другу.
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
Как примечание, я не трогал C++ в течение 3 лет, но я верю, что способ, которым я объясняю, все еще полезен для вас.
**** Новая идея здесь! ****
Я думаю, что предыдущие объяснения все еще верны, но я хочу объединить концепцию, чтобы иметь в виду лучше. (Так как я недавно использовал С# из-за моей стажировки)
- Правило большого пальца: сопоставьте
new
ключевое слово со " ссылочной стороной ".
Что это значит?
X b = new Y();
^^^^ ^^^^^^^^
Reference side Object side(= you)
Это означает, что тип ссылки важен, поэтому new
патриархален.
Кто твой справочник, ты тот, кто он есть.
new
средства УВАЖАЙТЕ СВОЮ ССЫЛКУ ! Вы - объектная сторона. С new
ты не важен, ты становишься немодным.
using System;
namespace ConsoleApplication1
{
class TestClass
{
public class BaseClass
{
public virtual void Foo() { Console.WriteLine ("BaseClass.Foo"); }
}
public class Overrider : BaseClass
{
public override void Foo() { Console.WriteLine ("Overrider.Foo"); }
}
public class Hider : BaseClass
{
public new void Foo() { Console.WriteLine ("Hider.Foo"); }
}
public static void Main(string[] args)
{
Overrider over = new Overrider();
BaseClass b1 = over;
over.Foo();
b1.Foo();
Hider h = new Hider();
BaseClass b2 = h;
h.Foo();
b2.Foo();
}
}
}
Более подробно, С# 7.0 в двух словах: окончательная ссылка, Джозеф Албахари, Бен Албахари
Ответ 8
Если ключевое слово override
используется в производном классе, то оно переопределяет родительский метод.
Если ключевое слово new
используется в производном классе, тогда выведите метод, спрятанный родительским методом.
Ответ 9
Ниже приведена статья vb.net, но я думаю, что объяснение новых переопределений vs очень легко понять.
https://www.codeproject.com/articles/17477/the-dark-shadow-of-overrides
В какой-то момент статьи есть следующее предложение:
В общем, Shadows предполагает, что функция, связанная с типом, вызывается, в то время как Overrides предполагает, что реализация объекта выполняется.
Принятый ответ на этот вопрос совершенен, но я думаю, что эта статья дает хорошие примеры, чтобы добавить лучшее значение в отношении различий между этими двумя ключевыми словами.
Ответ 10
Из всего этого новый наиболее запутан. Благодаря экспериментированию новое ключевое слово похоже на предоставление разработчикам возможности переопределить реализацию наследующего класса с реализацией базового класса, явно определяя тип. Это похоже на то, как думать наоборот.
В приведенном ниже примере результат вернет "Производный результат" до тех пор, пока тип явно не будет определен как тест 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";
}
}
Ответ 11
Функциональная разница не будет отображаться в этих тестах:
BaseClass bc = new BaseClass();
bc.DoIt();
DerivedClass dc = new DerivedClass();
dc.ShowIt();
В этом примере, вызванная Doit - это та, которую вы ожидаете назвать.
Чтобы увидеть разницу, вы должны это сделать:
BaseClass obj = new DerivedClass();
obj.DoIt();
Вы увидите, если вы запустите этот тест, который в случае 1 (как вы его определили) вызывается DoIt()
in BaseClass
, в случае 2 (как вы его определили) DoIt()
в DerivedClass
.
Ответ 12
У меня был тот же вопрос, и это действительно запутывало,
вы должны учитывать, что переопределить и новые ключевые слова работают только с объектами базового класса типа и значением производного класса. В этом случае вы увидите эффект переопределения и новый:
Поэтому, если у вас есть class A
и B
, B
наследуется от A
, вы создаете экземпляр объекта следующим образом:
A a = new B();
Теперь при вызове методы учитывают его состояние.
Переопределить: означает, что он расширяет функцию метода, а затем использует метод в производном классе, тогда как new сообщает компилятору скрыть метод в производном классе и используйте вместо этого метод в базовом классе.
Вот очень хороший взгляд на эту тему:
https://msdn.microsoft.com/EN-US/library/ms173153%28v=VS.140,d=hv.2%29.aspx?f=255&MSPPError=-2147217396
Ответ 13
В первом случае он вызовет метод производного класса 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