🧩 一、什么是方法隐藏(Method Hiding)
在 C# 中,如果子类定义了一个与父类同名的方法,并且使用 new
关键字, 那么子类的方法就会隐藏父类的方法。
这叫做 方法隐藏(Method Hiding),也称为 “静态多态” 或 “伪多态”。
💡 二、为什么叫“隐藏”?
因为父类的方法仍然存在,只不过:
当你用子类类型变量访问时,会调用子类的方法;
当你用父类类型变量访问时,会调用父类的方法。
🟡 它看起来像多态,但其实是编译时决定的(静态绑定), 并不是真正的“运行时多态”。
💻 三、示例代码对比说明
using System;
class Animal
{
public void Speak()
{
Console.WriteLine("动物在叫。");
}
}
class Dog : Animal
{
// new:隐藏父类方法
public new void Speak()
{
Console.WriteLine("狗在汪汪叫。");
}
}
class Program
{
static void Main()
{
Dog d = new Dog();
d.Speak(); // ✅ 输出:狗在汪汪叫。
Animal a = new Dog();
a.Speak(); // ⚠️ 输出:动物在叫。
}
}
🔍 四、运行机制分析
d.Speak()
:变量是Dog
类型 → 调用子类的Speak()
;a.Speak()
:变量是Animal
类型 → 调用父类的Speak()
。
🔸 因为
Speak()
不是virtual
的,所以编译器在编译阶段就决定调用哪个版本, 程序运行时不会再去“动态绑定”。
⚙️ 五、与真正多态(virtual/override)的对比
🧠 六、打个比喻帮助理解
✅ 七、总结一句话
方法隐藏实现的“多态”是表面上的多态(静态多态), 它只是让子类重新定义了一个同名方法, 并不会根据对象实际类型在运行时动态决定调用哪个方法。
换句话说:
new
是“重新写一个”,override
是“接着父类改一个”。
非常棒的疑问 👏👏👏 这个问题说明你已经开始真正理解 C# 的多态底层机制了。 你觉得“对象是 Dog 类型,为什么不调用 Dog 的方法?”——这个直觉没错! 但要弄清楚为什么结果不是那样,我们得看编译器是怎么决定调用哪个方法的。
让我们一步步剖析。👇
🧩 一、先看这段代码
class Animal
{
public void Speak()
{
Console.WriteLine("动物在叫。");
}
}
class Dog : Animal
{
public new void Speak()
{
Console.WriteLine("狗在汪汪叫。");
}
}
class Program
{
static void Main()
{
Animal a = new Dog();
a.Speak(); // ❓为什么输出:动物在叫?
}
}
🧠 二、从“变量类型” vs “对象类型”看区别
你认为应该执行 Dog.Speak()
是因为对象是 Dog
; 但 C# 编译器在决定调用哪个方法时,还要看方法是不是 虚方法 (virtual)。
⚙️ 三、关键点:C# 的“绑定时机”
在 C# 中,方法调用有两种绑定方式:
📘 四、解释你的代码行为
在你的代码中:
public void Speak() { ... } // 普通方法,不是 virtual
public new void Speak() { ... } // 隐藏父类方法
父类的
Speak()
不是虚方法 → 静态绑定;子类用
new
创建了一个新的方法,不是重写;当编译器看到
a.Speak()
时,它看到的是“变量类型是 Animal”, 所以它认为“我要调用 Animal 的 Speak()”, 并且在编译阶段就确定下来了。
🧩 运行时,虽然 a
实际上指向一个 Dog
对象, 但调用哪个方法已经在编译时固定死了——不会再去检查对象类型。
💬 五、如果想要“真正的多态”,需要用 virtual/override
只要你把父类方法改成 virtual
,子类用 override
,结果就完全不同了:
class Animal
{
public virtual void Speak()
{
Console.WriteLine("动物在叫。");
}
}
class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("狗在汪汪叫。");
}
}
class Program
{
static void Main()
{
Animal a = new Dog();
a.Speak(); // ✅ 现在输出:狗在汪汪叫。
}
}
现在:
Speak()
是虚方法;调用绑定被延迟到运行时;
程序运行时会检查对象实际类型 → 是
Dog
→ 调用Dog.Speak()
;✅ 真正的“运行时多态”。
🔍 六、总结对比
✅ 七、总结一句话
在 C# 中,如果方法不是
virtual
的,调用哪个方法是 编译时 决定的; 如果方法是virtual
的,调用哪个方法是 运行时 决定的。
所以:
Animal a = new Dog();
a.Speak();
之所以调用父类方法,是因为编译器“只看到了变量类型是 Animal”, 而且 Speak()
不是虚方法,它在编译时就已经锁定父类版本。
是否希望我帮你画一张图,显示 👉 “静态绑定(new) vs 动态绑定(override)” 的执行流程对比(编译时 vs 运行时调用决策)? 这张图特别适合教学或做 PPT。