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

时间:2012-08-27 13:05:29

标签: c# oop

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

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

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

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

7 个答案:

答案 0 :(得分:3)

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

如果对象是ClassA引用,则Get()将作为原始Get()处理,从而导致Get()中的覆盖ClassB

答案 1 :(得分:1)

ClassC.Get具有不同的签名(新字符串)和缺少覆盖,因此它不会覆盖ClassA.Get

答案 2 :(得分:1)

魔术被称为虚拟派遣。

ClassB会覆盖ClassA Get,这意味着系统知道ClassA的下属具有不同的Get实现。在Get类型的引用上调用ClassA时,它将检查引用是否实际为ClassB或后代,并执行该代码。

当然,这是(kinda-sorta)递归,所以如果ClassB有任何超过Get的下降,那么Get会被调用,等等...... / p>

此处的问题是,Get中定义的ClassCGetClassA中提到的ClassB不一样,而是一个全新的new方法(因此obj.Get()关键字)恰好具有相同的名称。

因此,当调用ClassC时,系统不会达到ClassB,但只能达到ClassC.Get()并执行该操作。如果您希望new被执行,可以将override更改为ClassC,或尝试通过((ClassC)obj).Get()引用进行调用,例如{{1}} }

答案 3 :(得分:0)

如果您不首先将其强制转换为ClassA,它将仅从ClassC调用Get()。 Get()的最后一个虚拟实现是来自ClassB的Get(),当你从A调用虚拟Get()方法时会调用它。

答案 4 :(得分:0)

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"

答案 5 :(得分:0)

此上下文中的new关键字是一个隐藏..

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

如果在派生类中声明此类方法而不覆盖基类方法和new关键字,编译器将生成警告

如果您不使用new修饰符,编译器将生成警告。

new modifier对编译器说复制成员不是意外,因此编译器不显示警告

答案 6 :(得分:0)

您可以先查看Versioning with override and newClassA引用将处理原始类中的Get()。由于此类型已覆盖,因此将调用ClassB的Get()。

ClassC引用会将Get()作为方法处理,因此ClassC中的方法将被调用

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"
}