所以我正在玩C#以查看它是否符合此帖子中的C ++行为:http://herbsutter.com/2013/05/22/gotw-5-solution-overriding-virtual-functions/ 当我遇到这种非常奇怪的行为时:
public class BaseClass
{
public virtual void Foo(int i)
{
Console.WriteLine("Called Foo(int): " + i);
}
public void Foo(string i)
{
Console.WriteLine("Called Foo(string): " + i);
}
}
public class DerivedClass : BaseClass
{
public void Foo(double i)
{
Console.WriteLine("Called Foo(double): " + i);
}
}
public class OverriddenDerivedClass : BaseClass
{
public override void Foo(int i)
{
base.Foo(i);
}
public void Foo(double i)
{
Console.WriteLine("Called Foo(double): " + i);
}
}
class Program
{
static void Main(string[] args)
{
DerivedClass derived = new DerivedClass();
OverriddenDerivedClass overridedDerived = new OverriddenDerivedClass();
int i = 1;
double d = 2.0;
string s = "hi";
derived.Foo(i);
derived.Foo(d);
derived.Foo(s);
overridedDerived.Foo(i);
overridedDerived.Foo(d);
overridedDerived.Foo(s);
}
}
输出
Called Foo(double): 1
Called Foo(double): 2
Called Foo(string): hi
Called Foo(double): 1
Called Foo(double): 2
Called Foo(string): hi
显然,它倾向于使用隐式转换的int来替换基类中更具体的Foo(int)。或者它是否隐藏了基类的Foo(int)?但那么:为什么不隐藏Foo(字符串)?感觉非常不一致...如果我重写Foo(int)也没关系;结果是一样的。谁能解释一下这里发生了什么?
(是的,我知道在派生类中重载基本方法是不好的做法--Liskov和所有 - 但我仍然不希望在OverriddenDerivedClass中调用Foo(int)?!)
答案 0 :(得分:8)
解释它如何适用于OverriddenDerivedClass
示例:
在这里查看成员查找的C#规范:http://msdn.microsoft.com/en-us/library/aa691331%28VS.71%29.aspx
定义了查找的完成方式。
特别是,请看这一部分:
首先,构造在T中声明的名为N的所有可访问(第3.5节)成员的集合,并构造T的基本类型(第7.3.1节)。包含覆盖修饰符的声明将从集合中排除。
在您的情况下,N
为Foo()
。由于Declarations that include an override modifier are excluded from the set
,override Foo(int i)
被排除在集合之外。
因此,只有未被覆盖的Foo(double i)
仍然存在,因此它就是被调用的那个。
这就是OverriddenDerivedClass
示例的工作原理,但这不是DerivedClass
示例的解释。
要解释一下,请看一下规范的这一部分:
接下来,将从集合中删除被其他成员隐藏的成员。
Foo(double i)
中的DerivedClass
隐藏了基类中的Foo(int i)
,因此会从集合中删除它。
这里棘手的部分是:
所有与基本类型S中声明的M具有相同签名的方法都将从集合中删除。
您可能会说“但等等!Foo(double i)
与<{1}}具有相同的签名,因此不应将其从集合中移除!”。< / p>
但是,因为存在从int到double的隐式转换,所以 被认为具有相同的签名,因此从集合中删除了Foo(int i)