为什么重载方法的优先级低于实例方法

12

我有一个基类A

public class A
{
    public virtual void Method(A parameter)
    {
        Console.WriteLine(MethodBase.GetCurrentMethod());
    }
    public virtual void Method(B parameter)
    {
        Console.WriteLine(MethodBase.GetCurrentMethod());
    }
}

继承了 B

public class B : A
{
    public virtual void Method(object parameter)
    {
        Console.WriteLine(MethodBase.GetCurrentMethod());
    }

    public override void Method(A parameter)
    {
        Console.WriteLine(MethodBase.GetCurrentMethod());
    }

    public override void Method(B parameter)
    {
        Console.WriteLine(MethodBase.GetCurrentMethod());
    }
}

具有扩展方法的静态类S

public static class S
{
    public static void Method(this B instance, B parameter)
    {
        Console.WriteLine(MethodBase.GetCurrentMethod());
    }
}

我们创建了一个类型为B的实例,并在其上调用Method,我们期望它将是public override void Method(B parameter),但实际结果是public virtual void Method(object parameter)

var b = new B();
b.Method(new B()); // B.Method (Object parameter) Why???
为什么编译器没有选择更合适的方法?更新:为什么它不是扩展方法?

3
B不是嵌套的,而是被继承的。 - H H
如果对象具有与扩展方法同名的方法,则不会调用扩展方法。这是为了避免破坏旧代码,其中期望调用旧方法而不是新的扩展方法。 - Ankush
1个回答

10
为什么编译器不选择更合适的方法?
因为它遵循语言规范的规则,在查找候选方法时会忽略在基类中原始声明的任何候选方法(即使它们在派生类中被覆盖),除非派生类没有适用的方法,此时搜索会向上移动到基类等。这是为了避免“脆弱的基类”问题而设计的,但面对被派生类覆盖的方法,我发现这很难接受。
C# 4 规范的相关部分是 7.4,其中最后一句是:
对于类型参数和接口以外的类型中的成员查找,以及严格单继承的接口中的成员查找(继承链中每个接口都恰好有零个或一个直接基接口),查找规则的效果只是派生成员隐藏了具有相同名称或签名的基础成员。
编辑:关于扩展方法...
为什么它不是扩展方法?
从规范的第7.6.5.2节:

In a method invocation (§7.5.5.1) of one of the forms

expr . identifier ( )
expr . identifier ( args )
expr . identifier < typeargs > ( )
expr . identifier < typeargs > ( args )

if the normal processing of the invocation finds no applicable methods, an attempt is made to process the construct as an extension method invocation

因此,扩展方法基本上只在万不得已的情况下使用。


我认为问题在于代码为什么调用了继承方法而不是扩展方法,但很难确定? - podiluska
@podiluska: 我认为扩展方法是一个误导。看起来 OP 并没有期望它被调用... - Jon Skeet
同样有趣的是为什么扩展方法也没有被调用。 - user854301
那个很简单:成员查找步骤仅在“正常”查找失败后尝试查找扩展方法。 - Jon Skeet
@Jon Skeet:我还是没弄明白。我已经读了关于“脆弱基类”的文章,觉得自己理解了。但在这里,我们在 B 类中重写了方法,并且它们适用,而且隐藏了与其名称或签名相同的基础成员,但为什么编译器不想接受它们呢?如果您能给出一些例子,说明为什么要这样做而不是其他方式,我将不胜感激。 - user854301

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接