使用params string []和继承的重载方法时的怪癖

时间:2011-12-06 06:30:37

标签: c#

我有一个这样的课

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" ) );

4 个答案:

答案 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,那么它将是首选的执行方法。