0

我有一个简单的基类和派生类:

class Base
{
    public virtual void Write(int value)
    {
        Console.WriteLine("base int: {0}", value);
    }

    public virtual void Write(string value)
    {
        Console.WriteLine("base string: {0}", value);
    }
}

class Derived : Base
{
    public override void Write(int value)
    {
        Console.WriteLine("derived int: {0}", value);
    }

    public virtual void Write(IEnumerable enumerable)
    {
        Console.WriteLine("derived IEnumerable: {0}", enumerable);
    }

    public virtual void Write(object o)
    {
        Console.WriteLine("derived obj: {0}", o);
    }
}

如果我运行这个:

    static void Main(string[] args)
    {
        Derived d = new Derived();
        Console.WriteLine("derived:");
        d.Write(42);
        d.Write("hello");

        Console.WriteLine("base:");
        Base b = d;
        b.Write(42);
        b.Write("hello");
    }

我得到:

derived:
derived obj: 42
derived IEnumerable: hello
base:
derived int: 42
base string: hello

但我希望“b.Write(42)”和“d.Write(42)”是相同的。字符串情况也是如此。

我不明白什么?鉴于我无法修改“Base”的约束,我如何才能使行为成为我所期望的?

更新:见埃里克的帖子

4

2 回答 2

3

发生这种情况是因为 C# 优先考虑在类型中声明的方法,包括覆盖方法。请参阅:C# 规范的第 7.3 节

这篇文解释得很好,也解释了原因。

这种非常不直观的行为可以通过以下两条规则来证明:

  1. 方法是否被覆盖是一个实现细节,应该允许在不破坏客户端代码的情况下进行更改。
  2. 不破坏继承类的基类更改不应破坏继承类的客户端。
于 2009-03-24T21:41:36.820 回答
1

string 可以隐式转换为 IEnumerable (of chars),但它的 ToString() 仍然返回字符串。因此,b.Write("hello");正在解析 IEnumerable 虚拟方法,因为它更接近被引用的类型。

我会验证,但如果您在派生类中覆盖字符串重载,它可能会在客户端代码中正确解析。

编辑

我错了,覆盖没有帮助。您可能必须重命名派生虚拟对象以避免冲突。

编辑二

以下确实有效,但它是超级黑客,我不喜欢它。将此方法添加到Derived

public new void Write(string value)
{
    base.Write(value);
}
于 2009-03-24T21:32:12.460 回答