gpt4 book ai didi

c# - 为什么不能从扩展类型的基类中调用扩展方法?

转载 作者:可可西里 更新时间:2023-11-01 02:59:16 25 4
gpt4 key购买 nike

我正在尝试通过覆盖索引器来添加在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()Base中都使用Sub调用扩展方法。
  • 我无法从base.ExtensionMethod()调用Sub
  • 我可以从Extensions.ExtensionMethod(this)Sub中使用Base调用扩展方法。
  • 我可以从this.ExtensionMethod()Sub中使用Base调用扩展方法。

  • 为什么是这样?

    我没有一个确定的答案,部分是因为可能没有答案:正如您可以在 this thread中阅读的那样,如果要以扩展方法样式进行调用,则必须添加 this.

    当您尝试使用扩展类型所在的扩展方法时(或因此而来的是从扩展方法中使用的类型派生的类型)时,编译器不会意识到这一点,而是会尝试调用它作为没有任何参数的静态方法。

    答案表明:他们(语言设计者)认为从类型内部支持隐式扩展方法(给野兽起个名字)并不是一个重要的用例场景,因为这会鼓励确实应该是实例方法和实例方法的扩展方法。这被认为完全没有必要。

    现在,很难找出幕后到底发生了什么,但是从一些实践中我们可以推断出 base.X()对我们没有帮助。我只能假定 base.X从odbbase类的上下文中将其虚拟调用作为 X()而不是 this.X()来执行。

    我想从子类中调用基类的扩展方法时该怎么办?

    坦白说,我还没有找到任何真正优雅的解决方案。考虑这种情况:
    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(),在子类和扩展方法之间形成代理。

  • 您可以为此使用 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#规范中的一个黑洞:几乎所有东西都可以正常工作,但是突然之间您不得不跳过一些麻烦,因为在这种情况下编译器不知道将当前实例注入(inject)方法调用中。

    实际上,智能感知也对这种情况感到困惑:

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

    一切都无法按预期进行。

    也许得出结论?

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

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

    或者,正如我们已经看到的那样,不可能使用可爱的语法。

    关于c# - 为什么不能从扩展类型的基类中调用扩展方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27883427/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com