2009-09-09 89 views
134

想知道有什么区别以下之间:差异覆盖

案例1:基地班

public void DoIt(); 

案例1:继承类

public new void DoIt(); 

案例2:基类

public virtual void DoIt(); 

案例2:继承类

public override void DoIt(); 

根据我运行的测试,案例1和案例2似乎都具有相同的效果。有没有区别,或者一个首选的方式?

+2

的许多问题,包括http://stackoverflow.com/questions/159978/c-keyword-usage-virtualoverride-vs-new – 2009-09-09 11:46:18

回答

170

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

new修饰符指示 编译器使用你的子类实现 而不是父类 实现。任何代码不是 引用您的类,但父类 类将使用父类 的实现。

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 blog

+4

重复'这指示编译器使用最后一个实现定义的method'。怎么能找到最后定义的一个方法的实现? – AminM 2014-03-05 19:30:53

+5

从具体类开始,检查它是否具有感兴趣的方法的实现。如果是这样,你就完成了。如果不是,则在继承层次结构中向上一步,即检查超类是否具有感兴趣的方法。继续,直到找到感兴趣的方法。 – csoltenborn 2014-10-24 08:16:30

7

尝试以下几点:(情形)

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

编辑:虚拟+覆盖在运行时(因此忽略真正覆盖虚拟方法)解决,而新的刚创造新的方法具有相同的名称,并隐藏了旧,它在编译时解析 - >你的编译器会调用它看到的方法

13

在第一种情况下,你将定义隐藏在父类中。这意味着只有当您将对象作为子类处理时才会调用它。如果您将该类转换为其父类型,则将调用该父类的方法。在第二种情况下,该方法被覆盖并且将被调用,而不管该对象是否作为子类或父类被强制转换。

149

虚拟:表示的方法可以通过继承

倍率被覆盖:覆盖的虚拟方法的功能在碱类,提供不同的功能。

隐藏原始方法(它不必是虚拟的),提供不同的功能。这应该只在绝对必要的地方使用。

当您隐藏某个方法时,仍然可以通过向上投射到基类来访问原始方法。这在某些情况下很有用,但很危险。

+0

为什么向上施放隐藏基方法的方法是危险的?或者你是否暗示一般来说铸造是危险的? – Mark 2016-11-07 02:39:05

+1

@Mark - 调用者可能不知道该实现,导致意外误用。 – 2016-11-07 16:26:06

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

如果您使用的情况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(); 
} 
+0

当你尝试你的代码时,你会将它显示为无效标记 – Learner 2013-02-13 10:54:26

+0

你能发布你收到的警告或错误吗?当我最初发布它时,此代码工作正常。 – 2013-02-13 12:55:38

+0

这应该全部粘贴在您的入门级(程序)中。这被删除,以允许在这个网站上更好的格式。 – 2013-02-13 12:57:44

1

如果在派生类中使用关键字override,那么它将覆盖父方法。

如果关键字new用于派生类,则派生方法由父方法隐藏。

0

功能相差不会显示在这些测试:

BaseClass bc = new BaseClass(); 

bc.DoIt(); 

DerivedClass dc = new DerivedClass(); 

dc.ShowIt(); 

在这种exmample,被称为是你期望被称为一个大一。

为了看你有没有做到这一点的区别:

BaseClass obj = new DerivedClass(); 

obj.DoIt(); 

你会看到,如果您运行测试的情况下,1(因为你定义它),在BaseClassDoIt()被调用时,在情况2中(如你所定义的),调用的DerivedClass

0

我有同样的问题,这真的令人困惑, 你应该考虑覆盖关键字类型的基类和派生类的价值的物品唯一的工作。在这种情况下,只有你会看到倍率和新的作用: 所以,如果你有class AB,从AB继承,那么你实例化一个对象是这样的:

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

我的方式记住这两个关键字,它们彼此相反的承担。

overridevirtual关键字必须定义为覆盖该方法。使用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 
1

下面的文章是在vb.net,但我认为关于新VS覆盖的解释是很容易掌握。

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

在文章中的某个时刻,有这样一句话:

在一般情况下,阴影假定与该类型相关联的功能 调用,而覆盖假定目标的实现是 执行。

这个问题的接受答案是完美的,但我认为这篇文章提供了很好的例子来为这两个关键词之间的差异添加更好的含义。

-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"; 
    } 
} 
+0

如果您反对,请添加您的评论。撞击和跑步是如此懦弱。 – usefulBee 2017-08-22 16:03:09

0

在第一种情况下,它将调用派生类DoIt()方法,因为new关键字隐藏了基类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()"); 
    } 
} 

,它会调用重写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