想知道以下几点之间的区别:
案例1:基类
public void DoIt();
案例1:继承类
public new void DoIt();
案例2:基类
public virtual void DoIt();
案例2:继承类
public override void DoIt();
基于我运行的测试,情况1和2似乎具有相同的效果。是否存在差异或首选方式?
答案 0 :(得分:231)
可以使用覆盖修改器 虚拟方法,必须使用 抽象方法。这表明了 编译器使用最后定义的 方法的实现。即使 该方法在引用时调用 它将使用的基类 实现覆盖它。
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
。
新修饰符指示 编译器使用您的子类实现 而不是父类 实现。任何不是的代码 引用你的类但是父类 class将使用父类 实施
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
。它们实际上是两个完全独立的方法,它们碰巧具有相同的名称,而不是覆盖基本方法的派生方法。
答案 1 :(得分:164)
虚拟:表示某个方法可能会被继承者覆盖
覆盖:覆盖基类中虚拟方法的功能,提供不同的功能。
新:隐藏原始方法(不必是虚拟的),提供不同的功能。这只应在绝对必要的地方使用。
隐藏方法时,仍然可以通过向上转换为基类来访问原始方法。这在某些情况下很有用,但很危险。
答案 2 :(得分:14)
在第一种情况下,您将定义隐藏在父类中。这意味着只有在将对象作为子类处理时才会调用它。如果将类强制转换为其父类型,则将调用父类的方法。在第二个实例中,该方法被重写,无论对象是否被强制转换为子类或父类,都将调用该方法。
答案 3 :(得分:7)
尝试以下:(case1)
((BaseClass)(new InheritedClass())).DoIt()
编辑:虚拟+覆盖在运行时被解析(因此覆盖真的覆盖了虚拟方法),而new只是创建具有相同名称的新方法,并隐藏旧方法,它在编译时解析 - >你的编译器会调用它'看到'的方法
答案 4 :(得分: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.
答案 5 :(得分:3)
如果您使用的情况下调用继承类的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();
}
答案 6 :(得分:3)
我的方法是牢记两个关键词彼此相反。
override
:必须定义virtual
关键字才能覆盖该方法。使用override
关键字的方法,无论引用类型(基类或派生类的引用)如何使用基类进行实例化,基类的方法都会运行。否则,派生类的方法会运行。
new
:如果关键字由方法使用,则与override
关键字不同,引用类型很重要。如果使用派生类实例化并且引用类型是基类,则运行基类的方法。如果使用派生类对其进行实例化,并且引用类型是派生类,则派生类的方法将运行。也就是说,它是override
关键字的对比度。 En passant,如果您忘记或省略向该方法添加新关键字,则编译器默认使用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年,但我相信我解释的方式对你有用。
答案 7 :(得分:1)
如果在derive类中使用了关键字override
,那么它将覆盖父方法。
如果在派生类中使用关键字new
,则派生父方法隐藏的方法。
答案 8 :(得分:1)
下面的文章是在vb.net中,但我认为关于新vs覆盖的解释很容易掌握。
https://www.codeproject.com/articles/17477/the-dark-shadow-of-overrides
在文章的某个时刻,有一句话:
通常,Shadows假定与类型相关联的函数是 调用,而Overrides假定对象实现是 执行。
这个问题的接受答案是完美的,但我认为这篇文章提供了很好的例子来为这两个关键词之间的差异增加更好的含义。
答案 9 :(得分:1)
在所有这些中,新是最令人困惑的。通过实验,new关键字就像给开发人员选择通过显式定义类型来覆盖继承类实现和基类实现。这就像反思一样。
在下面的示例中,结果将返回"派生结果"直到类型明确定义为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";
}
}
答案 10 :(得分: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()
中的{1}}。
答案 11 :(得分:0)
我有同样的问题而且真的令人困惑,
您应该考虑覆盖和新关键字,这些关键字仅适用于基类类型和派生类值的对象。在这种情况下,只有您才能看到覆盖和新效果:
因此,如果您有f<-function(u,v){
u*u*exp(2*v)+4*v*v*exp(-2*u)-4*u*v*exp(v-u)
}
x = seq(-2, 2, by = 0.5)
y = seq(-2, 2, by = 0.5)
z <- outer(x,y,f)
#Contour plot
contour(x,y,z)
#Persp plot
persp(x, y, z, phi = 25, theta = 55, xlim=c(-2,2), ylim=c(-2,2),
xlab = "U", ylab = "V",
main = "F(u,v)", col="yellow", ticktype = "detailed"
) -> res
和class A
,B
继承自B
,那么您实例化一个这样的对象:
A
现在,调用方法将考虑其状态。 覆盖:表示它扩展了方法的功能,然后它使用派生类中的方法,而 new 告诉编译器隐藏派生类中的方法而是使用基类中的方法。 这是一个很好的视角:
答案 12 :(得分:-1)
在第一种情况下,它将调用派生类DoIt()方法,因为new关键字隐藏了基类DoIt()方法。
在第二种情况下,它将调用overrideI 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
答案 13 :(得分:-1)