为什么代码从ClassB调用Get()?

问题描述:

当我调用下面的代码行时,它会从ClassB执行Get()。为什么代码从ClassB调用Get()?

该对象创建为ClassC,该方法的关键字为new。理想情况下,它应该从ClassC调用Get()

Main() 
{ 
     ClassA obj = new ClassC(); 
     lbl.Text = obj.Get(); 
} 


public class ClassA 
{ 
    public virtual string Get() 
    { 
    return "from A"; 
    } 
} 

public class ClassB : ClassA 
{ 
    public override string Get() 
    { 
    return "from B"; 
    } 
} 

public class ClassC : ClassB 
{ 
    public new string Get() 
    { 
    return "from C"; 
    } 
} 

任何人都可以请帮我找到原因。

+1

http://blogs.msdn.com/b/csharpfaq/archive/2004/03/12 /what-s-the-difference-between-code-override-code-and-code-new-code.aspx – asawyer

关键是ClassC中的new关键字。如果该对象被处理为ClassC,则编译器会使用它来掩盖原始的Get()函数。

如果对象是一个ClassA参考,Get()将作为原始Get()处理,并因此导致的首要Get()ClassB

+0

只有当你想声明一个名字为与基类中的'virtual'方法名称相同,并且您不希望重写它。** ..如果您声明此类方法时没有使用新的并覆盖关键字,编译器会给出** WARNING ** ..要隐藏**警告**您使用'new'关键字 – Anirudha

ClassC.Get有一个不同的签名(新字符串)和丢失覆盖,所以它不会覆盖ClassA.Get

+1

'new'不会改变方法的*签名*,它是一种不同类型的'override'。 – CodingGorilla

只有当您不先将它强制转换为ClassA时,它才会从ClassC调用Get()。 Get()的最后一个虚拟实现是ClassB的Get(),当您从A调用虚拟Get()方法时调用Get()方法。

new关键字会影响继承的方法。但是,只有在对象的编译时类型为ClassC时才这样。在你的情况下,编译时间类型是ClassA,这导致继承版本的使用。

这就是为什么在使用new关键字对继承成员进行遮蔽之前,您应该长时间思考的原因之一。根据变量持有实例的声明类型,可能会调用不同的方法:

ClassA a = new ClassC(); 
ClassC c = (ClassC)a; // Note, this is the SAME instance as in a 
Assert.AreSame(a, c); 

Console.WriteLine(a.Get()); // prints "from B" 
Console.WriteLine(c.Get()); // prints "from C" 
+0

当我在“lbl.Text = obj.Get();”中看到对象的类型时,它的类型是ClassC,所以当调用Get()时,它应该来自ClassC,因为对象的类型是ClassC。有点混乱。任何想法? –

+0

不是* runtime *类型是相关的,而是包含实例的变量的* compile *时间类型。所以,如果'obj'被声明为'ClassA'或'ClassB',它将不会调用'ClassC'的方法。 –

这种魔术叫做虚拟调度。

ClassB覆盖ClassAGet,这意味着系统知道ClassA具有具有不同执行Get一个descentant。当在ClassA类型的引用上调用Get时,它将检查引用实际上是ClassB还是后代,然后执行该代码。

当然,这是(胜负的)递归,所以如果ClassB有压倒Get任何descentants,那些Get的将被调用,等等...

的这里的问题是,Get定义在ClassCGet是不一样的,它是ClassAClassB中提到的,但是一个全新的方法(因此是new关键字),只是碰巧有相同的名字。

所以当obj.Get()被调用时,系统不会竟把ClassC但只起床ClassB并执行。如果你想有ClassC.Get()得到执行,你既可以改变newoverride,或尝试通过ClassC引用调用它,就像((ClassC)obj).Get()

+0

当我在“lbl.Text = obj.Get();”中看到对象的类型时,它的类型是ClassC,因此,当调用Get()时,它应该来自ClassC,因为对象的类型是ClassC。有点混乱。任何想法? –

+0

当然。当您看到实际对象的类型时,您正在查看运行时信息,即编译器根本无法使用。因为C#(大部分)是静态类型语言,所以决定在编译时调用什么以及如何完成。如果您需要调用在运行时发生,您可以始终将变量声明为“动态”,将决策转换为运行时。 – SWeko

在此背景下new关键字是藏起来..

当您的基类具有虚拟方法并且您希望与基类虚拟方法名称相同时,通常会使用此名称,而不会覆盖它

如果您在声明派生类中这样的方法不重写基类的方法和新的关键字,编译器会生成一个警告

如果不使用new修改编译器会生成一个警告。

new修饰符说要编译器的重复成员并非偶然,因此编译器不显示警告

您可能首先看看Versioning with override and newClassA参考将处理来自原始类的Get()。由于此类型已被覆盖,所以将调用ClassB的Get()。

ClassC参考将处理在ClassCGet()方法,所以方法将被调用

static void Main(string[] args) 
{ 
    ClassA obj = new ClassC(); 
    Console.WriteLine(obj.Get()); // will print out "from B" 

    ClassC obj2 = new ClassC(); 
    Console.WriteLine(obj2.Get()); // will print out "from C" 
}