为什么我不能从扩展类型的基类调用扩展方法? [英] Why can't I call an extension method from a base class of the extended type‏?

查看:151
本文介绍了为什么我不能从扩展类型的基类调用扩展方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying add the ability to lookup elements in a List<KeyValuePair<string,int>> by overriding the indexer.

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;
            }
        }
    }
}

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

For some reason, the compiler is throwing this error:


' System.Collections.Generic.List< System.Collections .Generic.KeyValuePair< string,int>> '不包含' Single '的定义。

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

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

While it is true that List<T> doesn't have that method, it should be visible because it is an extension method from the System.Linq namespace (which is included). Obviously using this.Single resolves the issue, but why is access via base an error?

C#规范的第7.6.8节说

Section 7.6.8 of the C# spec says


base.I时出现在类或结构中, I 必须表示该类或结构的基类成员。

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

这可能会阻止通过 base 访问扩展方法。但它也说

Which might seem to preclude access to extension method via base. However it also says


在绑定时,表格 base.I base [E] 的评估方式与编写完全相同((B)this).I ((B)this)[E] ,其中 B 是类或结构的基类构造发生了什么。因此, base.I base [E] 对应 this.I 这个[E] ,除了这个被视为基类的一个实例。

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 )).I 那么似乎应该允许扩展方法。

If base.I is just like ((B)this).I then it seems like extension methods should be allowed here.

有人能解释这两个陈述中明显的矛盾吗? / p>

Can anyone explain the apparent contradiction in these two statements?

推荐答案

考虑这种情况:

public class Base
{
    public void BaseMethod()
    {

    }
}

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

    }
}

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

以下是关于此代码的一些有趣断言:

Here are some interesting assertions about this code:


  • 我无法调用extensio n使用 ExtensionMethod()的方法既不来自 Base 也不是 Sub

  • 我无法从 Sub 调用 base.ExtensionMethod()

  • 可以使用 Extensions.ExtensionMethod(this) Sub Base

  • 可以使用扩展方法调用来自 Sub Base this.ExtensionMethod()

  • I cannot call the extension method using ExtensionMethod() from neither Base nor Sub.
  • I cannot call base.ExtensionMethod() from Sub.
  • I can call the extension method using Extensions.ExtensionMethod(this) from both Sub and Base.
  • I can call the extension method using this.ExtensionMethod() from both Sub and Base.

我没有确定的答案,部分是因为可能没有:你可以阅读这个帖子,你添加这个。如果你想打电话它在扩展方法样式中。

I don't have a conclusive answer, partly because there might not be one: as you can read in this thread, you have to add this. if you want to call it in the extension method style.

当你尝试使用它所在类型的扩展方法时(或 - 结果) ly - 从扩展方法中使用的类型派生的类型),编译器没有意识到这一点,并将尝试将其称为静态方法而不带任何参数。

When you're trying to use an extension method from the type it is in (or - consequently - from a type that is derived from the type used in the extension method), the compiler doesn't realize this and will try to call it as a static method without any arguments.

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

As the answer states: they [the language designers] felt it was not an important use case scenario to support implicit extension methods (to give the beast a name) from within the type because it would encourage extension methods that really should be instance methods and it was considered plain unnecessary.

现在,很难找到究竟发生了什么事情,但从一些游戏中我们可以推断出 base.X()对我们没有帮助。我只能假设 base.X 执行虚拟调用 X()而不是 this.X()来自基类的上下文。

Now, it is hard to find out what is happening exactly under the covers but from some playing around we can deduce that base.X() does not help us. I can only assume that base.X performs its virtual call as X() and not this.X() from the context of the baseclass.

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

Frankly, I haven't found any truly elegant solution. Consider this scenario:

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");
    }
}

有三种方法(不考虑反思)来调用 ExtensionMethod(Base)重载:

There are 3 ways (leaving aside reflection) to call the ExtensionMethod(Base) overload:


  • 调用 BaseMethod (),它在子类和extensionmethod之间形成代理。

  • Calling BaseMethod() which forms a proxy between the subclass and the extensionmethod.

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

You can use BaseMethod(), base.BaseMethod() and this.BaseMethod() for this since now you're just dealing with a normal instance method which in its turn will invoke the extension method. This is a fairly okay solution since you're not polluting the public API but you also have to provide a separate method to do something that should have been accessible in the context in the first place.


  • 使用扩展方法作为静态方法

您还可以使用原始方式通过跳过语法糖并直接编译它将被编译为扩展方法。现在你可以传入一个参数,这样编译器就不会感到困惑了。显然我们会传递一个当前实例的转换版本,所以我们正在针对正确的重载:

You can also use the primitive way of writing an extension method by skipping the syntactic sugar and going straight to what it will be compiled as. Now you can pass in a parameter so the compiler doesn't get all confused. Obviously we'll pass a casted version of the current instance so we're targetting the correct overload:

Extensions.ExtensionMethod((Base) this);




  • 使用 - 应该是相同的翻译 - base.ExtensionMethod()

    • Use the - what should be identical translation - of base.ExtensionMethod()
    • 这是受@Mike z关于语言规范的评论的启发说如下:

      This is inspired by @Mike z's remark about the language spec which says the following:


      在绑定时,表格 base.I base [E] 的评估方式与编写完全相同((B)this).I ((B)this)[E] ,其中 B 是类或结构的基类构造发生了什么。因此, base.I base [E] 对应 this.I 这个[E] ,除了这个被视为基类的一个实例。

      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(); 将完美地运作。

      The spec literally says that base.I will be invoked as ((B) this).I. However in our situation, base.ExtensionMethod(); will throw a compilation error while ((Base) this).ExtensionMethod(); will work perfectly.

      在文档或编译器中看起来有些问题但是这个结论应该由具有更深入知识的人来绘制。问题(寻呼Lippert博士)。

      It looks like something is wrong either in the documentation or in the compiler but that conclusion should be drawn by someone with deeper knowledge in the matter (paging Dr. Lippert).

      是的,我想说的是。它有点像C#规范中的黑洞:实际上一切都运行得很完美,但突然之间你必须跳过一些箍,因为编译器不知道在这种情况下在方法调用中注入当前实例。

      Yes, I would say it is. It kind of feels like a black hole within the C# spec: practically everything works flawlessly but then suddenly you have to jump through some hoops because the compiler doesn't know to inject the current instance in the method call in this scenario.

      事实上,intellisense也对这种情况感到困惑:

      In fact, intellisense is confused about this situation as well:

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

      We have already determined that that call can never work, yet intellisense believes it might. Also notice how it adds "using PortableClassLibrary" behind the name, indicating that a using directive will be added. This is impossible because the current namespace is in fact PortableClassLibrary. But of course when you actually add that method call:

      并且一切都不能按预期工作。

      and everything doesn't work as expected.

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

      The main conclusion is simple: it would have been nice if this niche usage of extension methods would be supported. The main argument for not implementing it was because it would encourage people to write extension methods instead of instance methods.

      这里显而易见的问题当然是你可能并不总是能够访问基类,这使得扩展方法成为必需,但是目前的实现是不可能的。

      The obvious problem here is of course that you might not always have access to the base class which makes extension methods a must but by the current implementation it is not possible.

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

      Or, as we've seen, not possibly with the cute syntax.

      这篇关于为什么我不能从扩展类型的基类调用扩展方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆