2

好的,回到基础。我想知道如何正确重载带有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");
    }
}

我的对象.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

为什么不呢?

4

1 回答 1

5

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

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

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

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

具体来说,第 7.6.5.1 节部分内容如下:

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

基类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()(因为string是更好的匹配。)C# 的设计者不想让完全不同团队中的另一个人默默地更改代码的含义。

有关脆弱的基类问题的更多信息,Eric Lippert 的(旧)博客有许多关于该主题的帖子。随便搜一下。

于 2013-06-05T17:02:26.790 回答