使用params关键字时,C#中的方法重载意外行为

时间:2013-06-05 16:48:04

标签: c# overloading params-keyword

好的,回到基础。我想知道如何使用params参数正确地重载方法。

这是我的情景。我从常规方法开始:

public void MyMethod(MyObject mo)
{
    // method body
}

我为它创建了一个看起来像这样的重载:

public void MyMethod(MyObject mo, params string[] fields)
{
    // new method body

    MyMethod(mo);
}

明显的意图是MyMethod(new MyObject());执行原始方法,MyMethod(new MyObject(), "field0"/*, etc...*/);执行重载方法。但这不是我发现的情况。

实际发生的是MyMethod(new MyObject());执行重载方法!

我不明白。在这种情况下,我将如何执行原始方法?

更新实际代码

好的,所以这里是产生所描述行为的实际代码。

Class1Base.cs:

public class Class1Base
{
    public virtual void MyMethod(MyObject ob)
    {
        Console.WriteLine("Called Class1Base");
    }
}

的Class1.cs:

public class Class1 : Class1Base
{
    public override void MyMethod(MyObject ob)
    {
        Console.WriteLine("called overridden method");
    }

    public void MyMethod(MyObject ob, params string[] fields)
    {
        Console.WriteLine("called OVERLOADED method");
    }
}

MyObject.cs:

public class MyObject
{
    public int Id { get; set; }
    public string Description { get; set; }
}

然后,当我以这种方式执行此代码时:

var myClass = new Class1();
var myObject = new MyObject();
myClass.MyMethod(myObject);
myClass.MyMethod(null);
myClass.MyMethod(null, "string");

控制台显示:

called OVERLOADED method
called OVERLOADED method
called OVERLOADED method

我原以为它会显示:

called overridden method
called overridden method
called OVERLOADED method

为什么不呢?

1 个答案:

答案 0 :(得分:5)

我认为你不是在告诉我们整个故事。 C#5规范的第7.3.5.2节(标题为“更好的功能成员”)部分说明:

  

•否则,如果MP以其正常形式适用且MQ具有   params数组,仅适用于其扩展形式,然后是MP   比MQ好。

这似乎就是这种情况,因为params版本需要“扩展”为零长度数组。实际上,在本地尝试代码会产生调用非params版本的预期结果。

更新:为了响应您的编辑,现在答案是明确的:您正在调用Class1中的方法,这意味着在执行重载解析时,标记为{{1的方法最初不考虑。并且由于非覆盖方法适用(尽管以其扩展形式),这是所选择的方法。

具体而言,第7.6.5.1节部分阅读:

  

•候选方法集合被简化为仅包含来自的方法   派生类型最多:对于集合中的每个方法C.F,其中C是   声明方法F的类型,在基类中声明的所有方法   从集合中删除C的类型。

基类override从候选集中排除,因此算法不会选择它。


这种行为背后的确切推理是避免出现“脆弱的基类”问题。假设我们有以下类层次结构:

MyMethod()

以下呼叫站点:

class A
{
}

class B : A
{
    public void MyMethod(object o) { }
}

这显然会解决new B().MyMethod("a string"); MyMethod()。但现在假设object的创建者(可能是另一个团队的成员)决定A也应该有A。所以他们改变了他们的课程:

MyMethod()

现在想象如果我们不从基类型中排除方法会发生什么。您最初解析为class A { public void MyMethod(string s); } 的电话会突然转为B.MyMethod()(因为A.MyMethod()是更好的匹配。)C#的设计者不想让其他人在一个完全不同的团队中,默默地改变代码的含义。

有关脆弱的基类问题的更多信息,Eric Lippert's (old) blog在该主题上有许多帖子。只是搜索一下。