我有一个简单的基类和派生类:
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。
答案 0 :(得分:3)
这是因为C#在其他任何事情之前考虑了在类型中声明的方法,包括覆盖方法。请参阅:Section 7.3 of the C# spec。
这个blog post很好地解释了它,并解释了原因。
这种非常不直观的行为可以通过以下两条规则来证明:
- 是否重写方法是实现细节 应该允许改变 不破坏客户端代码。
- 对不破坏继承类的基类的更改应该 不破坏继承的客户 类。
醇>
答案 1 :(得分:1)
string可以隐式地转换为IEnumerable(chars),但是它的ToString()仍然返回字符串。因此,b.Write("hello");
正在解析IEnumerable虚方法,因为它更接近引用的类型。
我将验证,但如果您覆盖派生类中的字符串重载,它可能会在客户端代码中正确解析。
修改强>
我错了,压倒无济于事。您可能必须重命名派生的虚拟对象以避免冲突。
编辑II
以下确实有效,但它是超级hackey,我不喜欢它。将此方法添加到Derived
:
public new void Write(string value)
{
base.Write(value);
}