解析虚拟覆盖中的“派生最多”方法

时间:2009-03-24 21:20:14

标签: c# override virtual-functions

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

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”的约束,我怎样才能使行为成为我所期待的?

更新:请参阅Eric's post

2 个答案:

答案 0 :(得分:3)

这是因为C#在其他任何事情之前考虑了在类型中声明的方法,包括覆盖方法。请参阅:Section 7.3 of the C# spec

这个blog post很好地解释了它,并解释了原因。

  

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

     
      
  1. 是否重写方法是实现细节   应该允许改变   不破坏客户端代码。
  2.   
  3. 对不破坏继承类的基类的更改应该   不破坏继承的客户   类。
  4.   

答案 1 :(得分:1)

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

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

修改

我错了,压倒无济于事。您可能必须重命名派生的虚拟对象以避免冲突。

编辑II

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

public new void Write(string value)
{
    base.Write(value);
}