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

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

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的post 。

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

这篇博文很好地解释了它,并解释了原因。

以下两条规则certificate了这种非常不直观的行为:

  1. 是否重写方法是应该允许在不破坏客户端代码的情况下更改的实现细节。
  2. 对不破坏inheritance类的基类的更改不应该破坏inheritance类的客户端。

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

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

编辑

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

编辑二

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

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