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

qua*_*els 2 c# overloading params-keyword

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

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

public void MyMethod(MyObject mo)
{
    // method body
}
Run Code Online (Sandbox Code Playgroud)

我为它创建了一个重载,如下所示:

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

    MyMethod(mo);
}
Run Code Online (Sandbox Code Playgroud)

显而易见的目的是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");
    }
}
Run Code Online (Sandbox Code Playgroud)

将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");
    }
}
Run Code Online (Sandbox Code Playgroud)

MyObject.cs:

public class MyObject
{
    public int Id { get; set; }
    public string Description { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

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

var myClass = new Class1();
var myObject = new MyObject();
myClass.MyMethod(myObject);
myClass.MyMethod(null);
myClass.MyMethod(null, "string");
Run Code Online (Sandbox Code Playgroud)

控制台显示:

called OVERLOADED method
called OVERLOADED method
called OVERLOADED method
Run Code Online (Sandbox Code Playgroud)

我原以为它会显示:

called overridden method
called overridden method
called OVERLOADED method
Run Code Online (Sandbox Code Playgroud)

为什么不呢?

dle*_*lev 5

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

•否则,如果MP以其正常形式适用且MQ具有params数组且仅适用于其扩展形式,则MP优于MQ.

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

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

具体而言,第7.6.5.1节部分内容如下:

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

基类MyMethod()从候选集中排除,因此算法不会选择它.


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

class A
{
}

class B : A
{
    public void MyMethod(object o) { }
}
Run Code Online (Sandbox Code Playgroud)

以下呼叫站点:

new B().MyMethod("a string");
Run Code Online (Sandbox Code Playgroud)

那显然会解决MyMethod()这个问题object.但现在假设A(也许谁在另一个团队工作)的创造者决定也A应该有一个MyMethod().所以他们改变了他们的课:

class A
{
    public void MyMethod(string s);
}
Run Code Online (Sandbox Code Playgroud)

现在想象如果我们不从基类型中排除方法会发生什么.您最初解决的呼叫B.MyMethod()将会解决A.MyMethod()(因为这string是一个更好的匹配.)C#的设计者不希望允许另一个完全不同的团队的人默默地改变您的代码的含义.

有关脆弱的基类问题的更多信息,Eric Lippert的(旧)博客有很多关于该主题的帖子.只是搜索一下.