C# 为什么标识符重用没有';如果从上置对象调用成员,则不会生效?

C# 为什么标识符重用没有';如果从上置对象调用成员,则不会生效?,c#,oop,C#,Oop,为什么输出是“这是孩子”,而不是“这是孩子” 类子类中的Print()是虚拟的。 我正在试图了解发生了什么。您正在调用父对象的overridedPrint方法 new如果您将该变量键入为Kid,则运算符将起作用。在这种情况下,new不是覆盖,而是标识符重用p属于父类。因此,编译器在Parent类中查找Print方法。由于此方法是虚拟的,因此它会在Kid类中找到一个被重写的方法。因为您没有覆盖而是替换了子对象中的打印方法,所以编译器不使用此方法。因为您使用了new关键字,这意味着您隐藏了继承的方法

为什么输出是“这是孩子”,而不是“这是孩子”

子类中的
Print()
是虚拟的。

我正在试图了解发生了什么。

您正在调用父对象的overrided
Print
方法


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();
    }