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

C# 使用带有params string[]和继承的重载方法时的怪癖,c#,C#,我上过这样的课 public class Child { public string ToXml() { return "Child : ToXml()"; } public string ToXml( params string[] fields ) { return "Child : ToXml(...)"; } } 创建一个类Child的实例并调用ToXml()将返回第一个重载函数,该函数非常好 var o

我上过这样的课

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()
但当我添加父类并将子类更改为:

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(...)
输出已更改为:

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(...)
我的问题是,为什么会有这样的行为?(我正在使用VS2010和.NET3.5)

我通过将第二个重载函数改为:(我去掉了params关键字)某种程度上“修复”了这个问题

这就引出了我的第二个问题(让我知道我是否应该把这篇文章分成两篇不同的文章),这两个函数之间的区别是什么

ToXml( params string[] fields )

当我这样调用函数时,它们似乎都能工作:

var obj = new Child();
Console.WriteLine( obj.ToXml( new [] { "foo", "bar" ) );

不确定第一个问题中发生了什么,但为了回答第二个问题,
params
关键字允许调用代码使用可变数量的参数调用函数,而不仅仅是数组。因此,您可以执行以下操作:

obj.ToXml("foo", "bar", "baz");
关于你的第一个问题:

例如,方法调用的候选集不包括标记为override的方法(第7.3节),如果派生类中的任何方法适用,则基类中的方法不是候选(第7.5.5.1节)


对于第二个问题,params修饰符使该参数充当varargs参数,这意味着它可以接收多个以逗号分隔的参数,并且数组是由编译器隐式创建的。

我相信第一个问题的答案是在没有参数的情况下调用ToXML的模糊性。有了这两个定义,您可以调用任一方法(由于params关键字,它也允许您不传递任何参数)

我认为调用ToXml的原因是子类的方法,因为它是子类的方法,而不是父类继承然后重写的方法

注意:如果您已经定义了不带参数的ToXml,那么它将是首选的执行方法

为什么会有这样的行为

不清楚你说的“像这样”是什么意思,因此,首先,让我们正确地描述该行为。您正在经历的行为是:为了重载解析的目的,派生类型上的适用候选方法总是优于基类型上的适用候选方法。此外,虚拟方法被认为是声明它的类型的方法,而不是覆盖它的类型

也不清楚“为什么”是什么意思?“为什么”问题很难回答;从某种意义上说,问题已经得到了回答:为什么编译器会表现出这种行为?因为这是重载解析的特定规则导致的结果。但是你可能会说这个问题已经被请求了;现在的问题是“那么,为什么规范是这样写的?”

规范是这样写的,因为这样做可以缓解脆弱的基类问题。脆弱的基类问题是指有两个团队,一个在基类上工作,另一个在派生类上工作的情况。如果在基类上工作的团队引入了新方法,那么这样做不应该改变使用派生类的代码的行为。如果这样,则基类更改“破坏”了派生类的行为。C#经过精心设计,以缓解脆弱的基类问题

即使不考虑脆弱的基类问题,在派生类上对方法进行优先级排序也是有意义的。派生类的开发人员比基类的开发人员拥有更多的信息。他们知道他们手中有一根香蕉;基类的开发人员只知道他们有一个果实调用应该转到开发人员编写的方法,在可能的情况下,开发人员可以获得更多信息

然后你可能会说“好吧,这个问题又被提出了;现在我想知道为什么减轻脆弱的基类问题很重要”。与其继续这种无限的倒退,我就不谈了;如果你想知道更多,那就问一个不那么模糊的问题

<>如果脆性基类故障减轻的主题使您感兴趣,请考虑阅读。

这些功能之间有什么区别

ToXml( params string[] fields )

不同之处在于,一个函数可能是扩展形式或正常形式的适用候选函数,而另一个函数只有一个适用候选函数形式。

听起来很奇怪,所以基本上,在为方法调用选择候选函数时,任何虚函数或抽象函数的“优先级”都较低。是否存在显式调用ToXml()的y方法“很多人认为这个行为是奇怪的,但它不是;看我为什么回答这个问题。回答你的第一个问题:不,虚拟性或抽象性与它无关。最初在基类上声明的方法的优先级较低。回答你的第二个问题:是的。把接收器扔到”谢谢你的澄清。来自C++背景的,我假设调用函数没有参数,就像我从来没有调用过的第二个重载函数,但是这个假设没有实际基础,除了我以前从未遇到过。