我有一个这样的课
public class Child
{
public string ToXml()
{
return "Child : ToXml()";
}
public string ToXml( params string[] fields )
{
return "Child : ToXml(...)";
}
}
创建类Child的实例并调用ToXml()返回第一个重载的函数,该函数很好并且花花公子。
var obj = new Child();
Console.WriteLine( obj.ToXml() );
输出:
Child : ToXml()
但是当我添加一个Parent类并将Child类更改为:
public class Parent
{
public virtual string ToXml()
{
return "Parent : ToXml()";
}
}
public class Child : Parent
{
public override string ToXml()
{
return "Child : ToXml()";
}
public string ToXml( params string[] fields )
{
return "Child : ToXml(...)";
}
}
输出已更改为:
Child : ToXml(...)
我的问题是,为什么这样的行为? (我使用的是使用.NET 3.5的VS2010)
我通过将第二个重载函数更改为“修复”了这个问题:(我拿出 params 关键字
public class Child : Parent
{
public override string ToXml()
{
return "Child : ToXml()";
}
public string ToXml( string[] fields )
{
return "Child : ToXml(...)";
}
}
这让我想到了我的第二个问题(让我知道我是否应该把它分成两个不同的帖子),这些功能有什么区别
ToXml( params string[] fields )
和
ToXml( string[] fields )
当我调用这样的函数时,它们似乎都有效:
var obj = new Child();
Console.WriteLine( obj.ToXml( new [] { "foo", "bar" ) );
答案 0 :(得分:7)
为什么这样的行为?
目前尚不清楚“像这样”是什么意思,所以首先,让我们正确地描述行为。您遇到的行为是:出于重载解析的目的,派生类型上的适用候选方法总是比基类型上适用的候选方法更好。而且,虚方法被认为是声明它的类型的方法,而不是覆盖它的类型。
你也不清楚“为什么”是什么意思? “为什么”这些问题很难回答;从某种意义上说,问题已经得到解答:为什么编译器会表现出这种行为?因为这是超载分辨率打破的指定规则的结果。但是你可能会说这个问题已被乞求;现在的问题是“那么为什么规范是这样编写的?”
规范是这样编写的,因为这样做可以缓解脆弱的基类问题。脆弱的基类问题是你有两个团队,一个在基类上工作,一个工作的情况在派生类上。 如果在基类上工作的团队引入了一个新方法,那么这样做不应该改变使用派生类的代码的行为。如果确实如此,那么基类更改已经“破坏”了派生类行为。 C#经过精心设计,可以缓解脆弱的基类问题。
即使将脆弱的基类问题排除在外,在派生类上优先考虑该方法也是有意义的。 派生类的开发人员比基类的开发人员拥有更多信息。他们知道他们手头有香蕉;基类的开发人员只知道他们有一个Fruit。调用应该转到开发人员编写的方法,他们尽可能提供更多信息。
然后你可能会说“嗯,这个问题再次被乞求;现在我想知道为什么减轻脆弱的基类问题很重要”。而不是继续这种无限的倒退,我只会把它留在那;如果你想了解更多,那就问一个不太模糊的问题。
如果脆弱基类失败缓解主题,请考虑阅读my lengthy series of articles on it。
功能之间有什么区别
不同之处在于,一个人可能是其扩展形式或正常形式的适用候选人,而另一个人只有一个适用的候选人形式。
答案 1 :(得分:4)
关于第一个问题,this is by design:
例如,方法调用的候选集不包括标记为override的方法(第7.3节),如果派生类中的任何方法适用,则基类中的方法不是候选方法(第7.5.5.1节)。
对于第二个问题,params修饰符使该参数充当varargs参数,这意味着它可以接收以逗号分隔的多个参数,并且数组由编译器隐式创建。
答案 2 :(得分:1)
不确定第一个问题中发生了什么,但要回答第二个问题,params
关键字允许调用代码使用可变数量的参数调用函数,而不仅仅是数组。所以你可以这样做:
obj.ToXml("foo", "bar", "baz");
答案 3 :(得分:0)
我相信你的第一个问题是通过调用没有参数的ToXML的模糊性来回答的。使用这两个定义,您可以调用任一方法(由于params关键字,它也允许您根本不传递任何参数)。
我认为正在调用的ToXml是Child类的方法的原因是,因为它是子类的方法,而不是由父类继承然后重写的方法。
注意:如果您定义了不带参数的ToXml,那么它将是首选的执行方法。