为什么代码从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";
}
}
任何人都可以请帮我找到原因。
关键是ClassC
中的new
关键字。如果该对象被处理为ClassC
,则编译器会使用它来掩盖原始的Get()
函数。
如果对象是一个ClassA
参考,Get()
将作为原始Get()
处理,并因此导致的首要Get()
在ClassB
。
只有当你想声明一个名字为与基类中的'virtual'方法名称相同,并且您不希望重写它。** ..如果您声明此类方法时没有使用新的并覆盖关键字,编译器会给出** WARNING ** ..要隐藏**警告**您使用'new'关键字 – Anirudha
ClassC.Get
有一个不同的签名(新字符串)和丢失覆盖,所以它不会覆盖ClassA.Get
。
'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"
当我在“lbl.Text = obj.Get();”中看到对象的类型时,它的类型是ClassC,所以当调用Get()时,它应该来自ClassC,因为对象的类型是ClassC。有点混乱。任何想法? –
不是* runtime *类型是相关的,而是包含实例的变量的* compile *时间类型。所以,如果'obj'被声明为'ClassA'或'ClassB',它将不会调用'ClassC'的方法。 –
这种魔术叫做虚拟调度。
ClassB
覆盖ClassA
Get
,这意味着系统知道ClassA
具有具有不同执行Get
一个descentant。当在ClassA
类型的引用上调用Get
时,它将检查引用实际上是ClassB
还是后代,然后执行该代码。
当然,这是(胜负的)递归,所以如果ClassB
有压倒Get
任何descentants,那些Get
的将被调用,等等...
的这里的问题是,Get
定义在ClassC
和Get
是不一样的,它是ClassA
和ClassB
中提到的,但是一个全新的方法(因此是new
关键字),只是碰巧有相同的名字。
所以当obj.Get()
被调用时,系统不会竟把ClassC
但只起床ClassB
并执行。如果你想有ClassC.Get()
得到执行,你既可以改变new
到override
,或尝试通过ClassC
引用调用它,就像((ClassC)obj).Get()
当我在“lbl.Text = obj.Get();”中看到对象的类型时,它的类型是ClassC,因此,当调用Get()时,它应该来自ClassC,因为对象的类型是ClassC。有点混乱。任何想法? –
当然。当您看到实际对象的类型时,您正在查看运行时信息,即编译器根本无法使用。因为C#(大部分)是静态类型语言,所以决定在编译时调用什么以及如何完成。如果您需要调用在运行时发生,您可以始终将变量声明为“动态”,将决策转换为运行时。 – SWeko
在此背景下new
关键字是藏起来..
当您的基类具有虚拟方法并且您希望与基类虚拟方法名称相同时,通常会使用此名称,而不会覆盖它。
如果您在声明派生类中这样的方法不重写基类的方法和新的关键字,编译器会生成一个警告
如果不使用new
修改编译器会生成一个警告。
new修饰符说要编译器的重复成员并非偶然,因此编译器不显示警告
您可能首先看看Versioning with override and new。 ClassA
参考将处理来自原始类的Get()
。由于此类型已被覆盖,所以将调用ClassB
的Get()。
ClassC
参考将处理在ClassC
Get()
为新方法,所以方法将被调用
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"
}
http://blogs.msdn.com/b/csharpfaq/archive/2004/03/12 /what-s-the-difference-between-code-override-code-and-code-new-code.aspx – asawyer