You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何C#编译器会调用未显式引用类的最具体重写实现?

为什么A a = new C(); a.helloWorld();会调用B的实现?

要搞懂这个行为,得把virtual/override/new的本质和C#的虚方法机制说透:

核心规则梳理

  • virtual:在基类中标记方法为可被重写,同时为该方法建立虚方法表(vtable),多态调用时会从这里找实际的实现。
  • override:在派生类中延续虚方法的多态链——它会替换虚方法表中对应位置的实现,所有通过基类引用的多态调用都会指向这个新实现。
  • new:这不是重写,是隐藏基类的同名成员——它会创建一个全新的、和基类虚方法无关的方法,这个方法不会进入虚方法表,只有当引用类型是当前派生类(或向下转型到该类)时,才会被调用。

对应你的继承链分析

  1. 基类A的helloWorld是virtual,所以它的虚方法表里有一个指向A.helloWorld的条目。
  2. B用override重写后,B的虚方法表里,helloWorld的条目被替换成B.helloWorld——这意味着所有通过A或B引用指向B实例的调用,都会走B的实现。
  3. C用new声明helloWorld时,并没有修改虚方法表里的条目——C的虚方法表中,helloWorld依然继承自B的override版本。new只是在C类的"非多态调用路径"上,把同名方法替换成了自己的实现。

解释你的代码行为

当你写A a = new C(); a.helloWorld();时:

  • 变量a是A类型的引用,调用helloWorld会触发多态调用,也就是去查实例(C对象)的虚方法表。
  • 而C的虚方法表里,helloWorld的条目还是B.helloWorld(因为C没override这个方法,只是用new隐藏了自己的同名方法),所以最终调用的是B的实现。

如果换成C c = new C(); c.helloWorld();,因为引用类型是C,会直接调用C用new声明的方法;如果写成((B)c).helloWorld();,又会回到虚方法表调用B的实现。

C#文档的相关定义

C#语言规范明确说明:使用new关键字声明的成员,是对基类同名成员的隐藏,而非覆盖。通过基类引用访问该成员时,会使用基类的实现;只有通过派生类引用访问时,才会使用new声明的成员。这就是你看到这个行为的官方依据。

内容的提问来源于stack exchange,提问作者Mr Guliarte

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 14:25:23