C# 为什么标识符重用没有';如果从上置对象调用成员,则不会生效?
为什么输出是“这是孩子”,而不是“这是孩子” 类C# 为什么标识符重用没有';如果从上置对象调用成员,则不会生效?,c#,oop,C#,Oop,为什么输出是“这是孩子”,而不是“这是孩子” 类子类中的Print()是虚拟的。 我正在试图了解发生了什么。您正在调用父对象的overridedPrint方法 new如果您将该变量键入为Kid,则运算符将起作用。在这种情况下,new不是覆盖,而是标识符重用p属于父类。因此,编译器在Parent类中查找Print方法。由于此方法是虚拟的,因此它会在Kid类中找到一个被重写的方法。因为您没有覆盖而是替换了子对象中的打印方法,所以编译器不使用此方法。因为您使用了new关键字,这意味着您隐藏了继承的方法
子类中的Print()
是虚拟的。
我正在试图了解发生了什么。您正在调用父对象的overridedPrint
方法
new
如果您将该变量键入为Kid
,则运算符将起作用。在这种情况下,new
不是覆盖,而是标识符重用p
属于父类
。因此,编译器在Parent
类中查找Print
方法。由于此方法是虚拟的,因此它会在Kid
类中找到一个被重写的方法。因为您没有覆盖而是替换了子对象中的打印方法,所以编译器不使用此方法。因为您使用了new关键字,这意味着您隐藏了继承的方法并提供了新的实现。这通常被称为隐藏父成员。来自MSDN():
“当对派生实例调用DoWork时,C#编译器将首先尝试使调用与最初在派生实例上声明的DoWork版本兼容。重写方法不被认为是在类上声明的,它们是在基类上声明的方法的新实现。只有当C#编译器无法将方法调用与派生上的原始方法匹配时,它才会尝试将调用与具有相同名称和兼容参数的重写方法匹配。”
看起来您在chid中使用了“new”,但将p声明为父级意味着p无法在子级中看到打印,因为它不是继承层次结构的一部分
class Program
{
static void Main(string[] args)
{
Parent p = new Child();
p.Print();
}
}
class Parent
{
public virtual void Print()
{
Console.WriteLine("This is parent.");
}
}
class Kid:Parent
{
public override void Print()
{
Console.WriteLine("This is Kid.");
}
}
class Child : Kid
{
public new virtual void Print()
{
Console.WriteLine("This is Child.");
}
}
…显然改变了一切。以下内容过度简化了一个可能的实现,并将其视为事实,但应该足以提供一个工作的心智模型
调用代码“了解”类时,它知道以下事项:
可以在对象位置的特定偏移处访问字段。因此,例如,如果一个对象位于地址120,并且有两个整数字段,那么它可能能够在地址124访问其中一个。如果相同类型的另一个对象位于地址140,则等效字段将位于144
非虚拟方法(一个或两个方法的属性可以被视为语法糖)是位于特定地址的函数,它引用了您正在调用的对象(this
,来自该方法)以及该函数的其他参数
虚拟方法与上述类似,但可以通过查看与类关联的表中的特定偏移量来找到它们的地址,该表的地址也将是与类地址的特定偏移量
在这种情况下,Kid
有一个方法表,它是Parent
的超集(它可以添加更多的方法),并且对于那些它没有覆盖的方法,它有相同的函数地址(在它上面调用Equals
使用与在Parent
上调用Equals相同的函数),但它所覆盖的地址不同(Print()
)
因此,如果您有一个Kid
,那么无论您是通过Parent
引用还是Kid
引用获得它,调用Print()
将查找同一个表,查找Print()
方法的位置,并调用它
对于Child
,在Print
方法上使用了new
。这告诉编译器我们特别需要一个不同的表。因此,如果调用Print()
通过子
引用,它会查找子
特定的表,并调用它找到的方法。如果我们通过子
或父
引用调用它,那么我们甚至不知道我们可以使用子
特定的表,我们会在我们知道的表中查找函数de>Kid
和Parent
分别具有,并调用找到的函数(在Kid
中定义的函数)
通常,应避免使用新的
一个是向后兼容性。例如,如果Child
有一个Name
属性,然后后来Parent
的代码被更改,因此它也有一个Name
属性,那么我们就有了冲突。因为Child
的Name
不是覆盖,所以它会被视为具有new
属性,而是gi这给了我们一个警告,因为这是使用旧方式的代码和知道新的名称的在Parent
上的代码可以共存的唯一方式。如果我们回来重新编译Child
,我们可能应该重构,这样它就没有自己的名称了(如果Parent
上的代码符合我们的要求),重构所以它是一个覆盖,重构到完全不同的东西,或者添加新的,表明这是我们想要的东西,尽管它不太理想
另一种情况是new
允许基类方法允许的相同行为的更具体形式,但在逻辑上是兼容的(因此用户不会感到惊讶)。后者应该放在半高级技术框中,不能轻易完成。它也应该这样评论,因为大多数时候看到新的
意味着你处理的事情充其量是一种折衷,可能需要改进
(旁白:我是唯一一个在看到《儿童》时想到小报的人吗?你的问题(他们是做什么的)与标题(如何运作)不匹配。但这两个问题(很多次)重复。你不清楚你不明白的地方。@user1559463我已经编辑了你的问题标题。现在更适合w
static void Main(string[] args)
{
Parent p = new Child();
p.Print();
Child c = (Child) p;
c.Print();
}