Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么代码从ClassB调用Get()?_C#_Oop - Fatal编程技术网

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

C# 为什么代码从ClassB调用Get()?,c#,oop,C#,Oop,当我调用下面的代码行时,它从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执行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()
处理,从而导致在
ClassB

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

只有在您不首先将其强制转换为ClassA的情况下,它才会从ClassC调用Get()。
Get()的最后一个虚拟实现是ClassB中的Get(),当您从A调用virtual 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"

这种魔力叫做虚拟调度

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

当然,这是(有点)递归的,所以如果
ClassB
有任何超过
Get
的描述,那么那些
Get
的将被调用,等等

这里的问题是,在
ClassC
中定义的
Get
ClassA
ClassB
中引用的
Get
不同,而是一种全新的方法(即
new
关键字),恰好具有相同的名称


因此,当调用
obj.Get()
时,系统不会到达
ClassC
,而只到达
ClassB
并执行该操作。如果要执行
ClassC.Get()
Get,可以将
new
更改为
override
,或者尝试通过
ClassC
引用调用它,如
((ClassC)obj)。Get()

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

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

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

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


新修饰符对编译器说,复制成员不是意外事件,因此编译器不会显示警告,您可以先查看一下
ClassA
reference将处理原始类中的
Get()
。由于此类型已被重写
ClassB
的Get()将被调用

ClassC
reference将
Get()
处理为new方法,因此将调用
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"
}

new
不会更改方法的签名,这是另一种类型的
重写
。只有当您希望使用与基类中的
虚拟
方法名称相同的名称来声明方法,并且不希望重写它时,才能在此上下文中使用
新建
。如果您在没有new and override关键字的情况下声明此类方法,编译器发出警告..要隐藏您使用的警告,请使用
new
关键字当我在“lbl.Text=obj.Get();”处看到对象类型时,它是ClassC类型的,因此,当调用Get()时,它应该来自ClassC,因为对象的类型是ClassC。有点困惑。知道吗?当然知道。当您看到实际对象的类型时,您正在查看编译器根本没有的运行时信息。因为C#是(大部分)静态类型语言,所以在编译时决定调用什么以及如何执行。如果您需要在运行时调用,您可以始终将变量声明为
dynamic
将决策转移到运行时。当我在“lbl.Text=obj.Get();”处看到对象的类型时,它是ClassC类型的,因此,当调用Get()时,它应该来自ClassC,因为对象的类型是ClassC。有点困惑。知道吗?不是运行时类型相关,而是包含实例的变量的编译时类型。因此,如果
obj
被声明为
ClassA
ClassB
它将不会调用
ClassC
的方法。