Administrator
发布于 2025-10-14 / 0 阅读
0
0

多态-方法隐藏new是什么

🧩 一、什么是方法隐藏(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(隐藏)

virtual/override(重写)

关键字

new

virtual + override

调用绑定

编译时绑定(静态)

运行时绑定(动态)

父类引用调用子类方法?

❌ 不会

✅ 会

是否是真正的多态

❌ 否

✅ 是

用途

重定义父类方法

实现多态行为


🧠 六、打个比喻帮助理解

场景

方法隐藏 (new)

方法重写 (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 “对象类型”看区别

名称

类型

变量 a

Animal 类型

实际对象

Dog 类型

你认为应该执行 Dog.Speak() 是因为对象是 Dog; 但 C# 编译器在决定调用哪个方法时,还要看方法是不是 虚方法 (virtual)


⚙️ 三、关键点:C# 的“绑定时机”

在 C# 中,方法调用有两种绑定方式:

绑定方式

时机

关键字

说明

✅ 动态绑定

运行时决定

virtual / override

真正的多态

⚠️ 静态绑定

编译时决定

普通方法 / new 隐藏

伪多态(编译时已定)


📘 四、解释你的代码行为

在你的代码中:

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

  • ✅ 真正的“运行时多态”。


🔍 六、总结对比

特征

普通方法 + new

虚方法 + override

调用绑定时间

编译时

运行时

调用依据

变量的声明类型

对象的实际类型

是否真正多态

❌ 否(伪多态)

✅ 是(真多态)

输出结果(Animal a = new Dog()

调用 Animal.Speak()

调用 Dog.Speak()


✅ 七、总结一句话

在 C# 中,如果方法不是 virtual 的,调用哪个方法是 编译时 决定的; 如果方法是 virtual 的,调用哪个方法是 运行时 决定的。

所以:

Animal a = new Dog();
a.Speak();

之所以调用父类方法,是因为编译器“只看到了变量类型是 Animal”, 而且 Speak() 不是虚方法,它在编译时就已经锁定父类版本。


是否希望我帮你画一张图,显示 👉 “静态绑定(new) vs 动态绑定(override)” 的执行流程对比(编译时 vs 运行时调用决策)? 这张图特别适合教学或做 PPT。


评论