C# - 多态性混淆了重载和方法隐藏

本文关键字:重载 方法 隐藏 多态性 | 更新日期: 2023-09-27 18:00:51

我有一个名为Item的类,它充当抽象类,但没有定义为抽象类。此类具有MaxPerStack属性:

public class Item
{
    public short MaxPerStack
    {
        get
        {
            return this.Data.MaxPerStack;
        }
    }
}

我还有一个名为 Equip 的类,它派生自 Item 类,并且还有一个名为 MaxPerStack 的属性,该属性声明为 new

public class Equip : Item
{
    public new short MaxPerStack
    {
        get
        {
            return 1;
        }
    }
}

我有以下方法称为Add,它使用itemMaxPerStack属性:

public void Add(Item item)
{
    Console.WriteLine("Stack: {0}.", item.MaxPerStack);
}

当我这样做时:

Add(new Equip());

它归ItemMaxPerStack属性,而不是Equip属性。

为什么会这样?

C# - 多态性混淆了重载和方法隐藏

因为你没有将Item.MaxPerStack标记为virtual,也没有将Equip.MaxPerStack标记为override。 你需要做这两件事才能获得你期望的行为。

通过将基类型的方法隐式标记为sealed并将派生类型显式标记为new可以阻止虚拟调度,这是用于确保在基类型上调用的方法将执行与对象的实际运行时类型关联的实现的确切机制,而不是将方法静态绑定到对象的编译时类型中的实现。

这里的问题是您的Equip类替换了在Item类上定义的 MaxPerStack 属性。

public class Equip : Item
{
    public new short MaxPerStack
    {
        get
        {
            return 1;
        }
    }
}

MSDN 告诉您:

如果派生类中的方法前面有 new 关键字,则该方法被定义为独立于基类中的方法。

..

使用 new 关键字告诉编译器您的定义隐藏了基类中包含的定义。这是默认行为。

这也适用于属性。这告诉您Equip将具有自己的非相关MaxPerStack版本。因此,当您调用Item.MaxPerStack时,您正在访问Item类上的属性,而不是Equip类。不管你传入的Item是否真的是一个Equip类。

若要解决此问题,可以将 MaxPerStack 属性设置为虚拟,然后在 Equip 类中重写它。

public class Item
{
    public virtual short MaxPerStack
    {
        get
        {
            return this.Data.MaxPerStack;
        }
    }
}
public class Equip : Item
{
    public override short MaxPerStack
    {
        get
        {
            return 1;
        }
    }
}

现在,当您提供Equip作为参数时 Add(Item)Add 方法将改为访问 Equip 类上的 MaxPerStack 属性。

MSDN 告诉您:

如果派生类中的方法前面带有 override 关键字,则派生类的对象将调用该方法而不是基类方法。