为什么我不能从扩展类型的基础 class 调用扩展方法?

Why can't I call an extension method from a base class of the extended type‏?

我正在尝试通过覆盖索引器来添加在 List<KeyValuePair<string,int>> 中查找元素的功能。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    public class MyList : List<KeyValuePair<string, int>>
    {
        public int this[string key]
        {
            get
            {
                return base.Single(item => item.Key == key).Value;
            }
        }
    }
}

出于某种原因,编译器抛出此错误:

'System.Collections.Generic.List<System.Collections.Generic.KeyValuePair<string,int>>' does not contain a definition for 'Single'.

虽然 List<T> 确实没有该方法,但它应该是可见的,因为它是 System.Linq 命名空间(包含在内)的扩展方法。显然使用 this.Single 可以解决问题,但为什么通过 base 访问会出错?

C# 规范的第 7.6.8 节说

When base.I occurs in a class or struct, I must denote a member of the base class of that class or struct.

这似乎排除了通过 base 访问扩展方法。但是它也说

At binding-time, base-access expressions of the form base.I and base[E] are evaluated exactly as if they were written ((B)this).I and ((B)this)[E], where B is the base class of the class or struct in which the construct occurs. Thus, base.I and base[E] correspond to this.I and this[E], except this is viewed as an instance of the base class.

如果 base.I((B)this).I 一样,那么这里似乎应该允许扩展方法。

谁能解释这两个陈述中明显的矛盾?

考虑这种情况:

public class Base
{
    public void BaseMethod()
    {

    }
}

public class Sub : Base
{
    public void SubMethod()
    {

    }
}

public static class Extensions
{
    public static void ExtensionMethod(this Base @base) { }
}

这里有一些关于这段代码的有趣断言:

  • 我无法使用 ExtensionMethod()BaseSub 调用扩展方法。
  • 我无法从 Sub 调用 base.ExtensionMethod()
  • 可以使用Extensions.ExtensionMethod(this)SubBase调用扩展方法。
  • 可以使用this.ExtensionMethod()SubBase调用扩展方法。

这是为什么?

我没有确定的答案,部分原因是可能没有:正如您在 this thread 中看到的那样,您 必须 添加 this.如果你想在扩展方法风格中调用它。

当你试图从它所在的类型(或者 - 因此 - 从派生自扩展方法中使用的类型的类型)中使用扩展方法时,编译器不会意识到这一点并且将尝试将其作为不带任何参数的静态方法调用。

正如答案所述:他们 [语言设计者] 认为从类型中支持隐式扩展方法(给野兽起个名字)不是一个重要的用例场景,因为它会鼓励扩展方法真正应该是实例方法,它被认为是不必要的。

现在,很难找出幕后到底发生了什么,但通过一些尝试,我们可以推断出 base.X() 对我们没有帮助。我只能假设 base.X 作为 X() 而不是 this.X() 从基础 class.

的上下文中执行其虚拟调用

想从subclass调用baseclass的扩展方法怎么办?

坦率地说,我还没有找到任何真正优雅的解决方案。考虑这种情况:

public class Base
{
    protected void BaseMethod()
    {
        this.ExtensionMethod();
    }
}

public class Sub : Base
{
    public void SubMethod()
    {
        // What comes here?
    }
}

public static class Extensions
{
    public static void ExtensionMethod(this Base @base) 
    { 
        Console.WriteLine ("base");
    }

    public static void ExtensionMethod(this Sub sub) 
    {
        Console.WriteLine ("sub");
    }
}

有 3 种方法(不考虑反射)调用 ExtensionMethod(Base) 重载:

  • 调用 BaseMethod() 形成子class 和扩展方法之间的代理。

您可以为此使用 BaseMethod()base.BaseMethod()this.BaseMethod(),因为现在您只是在处理一个普通的实例方法,而该实例方法又会调用扩展方法。这是一个相当不错的解决方案,因为您没有污染 public API,但您还必须提供一个单独的方法来做一些本来应该在上下文中可以访问的事情。

  • 将扩展方法用作静态方法

您还可以使用原始方式编写扩展方法,方法是跳过语法糖并直接将其编译为什么。现在你可以传入一个参数,这样编译器就不会搞混了。显然,我们将传递当前实例的转换版本,因此我们的目标是正确的重载:

Extensions.ExtensionMethod((Base) this);
  • 使用base.ExtensionMethod()
  • 的-什么应该是相同的翻译-

这受到@Mike z 对语言规范的评论的启发,该评论如下:

At binding-time, base-access expressions of the form base.I and base[E] are evaluated exactly as if they were written ((B)this).I and ((B)this)[E], where B is the base class of the class or struct in which the construct occurs. Thus, base.I and base[E] correspond to this.I and this[E], except this is viewed as an instance of the base class.

规范字面上说 base.I 将被调用为 ((B) this).I。然而,在我们的情况下,base.ExtensionMethod(); 会抛出编译错误,而 ((Base) this).ExtensionMethod(); 会完美运行。

文档或编译器中似乎有问题,但该结论应该由对此事有更深入了解的人得出(寻呼 Lippert 博士)。

这不是很混乱吗?

是的,我会说是。这有点像 C# 规范中的黑洞:实际上一切都完美无缺,但突然间你不得不跳过一些障碍,因为编译器不知道在这种情况下将当前实例注入方法调用中。

其实intellisense也对这种情况很困惑:

我们已经确定该调用永远无法工作,但 intellisense 认为它可能。还要注意它如何在名称后面添加“using PortableClassLibrary”,表示将添加 using 指令。这是不可能的,因为当前命名空间实际上是 PortableClassLibrary。但是当然,当您实际添加该方法调用时:

一切都没有按预期工作。

或许有结论?

主要结论很简单:如果支持扩展方法的这种利基用法就好了。不实现它的主要论点是因为它会鼓励人们编写扩展方法而不是实例方法。

这里明显的问题当然是您可能并不总是可以访问基础 class,这使得扩展方法成为必须,但根据当前的实现,这是不可能的。

或者,正如我们所见,不可能使用可爱的语法。