我尝试通过覆盖索引器来添加查找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;
}
}
}
}
由于某种原因,编译器抛出了这个错误:
&#39;
的定义System.Collections.Generic.List<System.Collections.Generic.KeyValuePair<string,int>>
&#39;不包含&#39;Single
&#39;。
虽然List<T>
确实没有该方法,但它应该是可见的,因为它是来自System.Linq
命名空间(包括在内)的扩展方法。显然使用this.Single
可以解决问题,但为什么通过base
访问会出错?
C#规范的第7.6.8节说
当
base.I
出现在类或结构中时,I
必须表示该类或结构的基类成员。
这似乎阻止了通过base
访问扩展方法。不过它也说
在绑定时,
base.I
和base[E]
形式的基本访问表达式的计算方式与((B)this).I
和((B)this)[E]
一样,其中{{1}是构造发生的类或结构的基类。因此,B
和base.I
对应base[E]
和this.I
,但this[E]
被视为基类的实例。
如果this
与base.I
类似,那么此处似乎应该允许使用扩展方法。
有谁可以解释这两个陈述中明显的矛盾?
答案 0 :(得分:24)
考虑这种情况:
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()
来调用扩展方法。我没有得出确凿的答案,部分原因是可能没有答案:您可以在this thread中阅读, 添加Sub
你想用扩展方法风格来调用它。
当你尝试使用它所在类型的扩展方法时(或 - 因此 - 来自扩展方法中使用的类型派生的类型),编译器没有意识到这将并尝试将其称为静态方法,不带任何参数。
正如答案所述:他们[语言设计者]认为从类型中支持隐式扩展方法(给兽名字)不是一个重要的用例场景,因为它会鼓励真正应该的扩展方法实例方法,它被认为是不必要的。
现在,很难找到正是在幕后发生的事情,但从一些游戏中我们可以推断Base
对我们没有帮助。我只能假设this.
从基类的上下文中执行虚拟调用base.X()
而不是base.X
。
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");
}
}
,它在子类和extensionmethod之间形成代理。 您可以使用ExtensionMethod(Base)
,BaseMethod()
和BaseMethod()
,因为现在您只是处理普通的实例方法,而后者又会调用扩展方法。这是一个相当好的解决方案,因为您不会污染公共API,但您还必须提供一个单独的方法来执行本来应该在上下文中可访问的内容。
您还可以使用原始的方式编写扩展方法,方法是跳过语法糖并直接进行编译。现在你可以传入一个参数,这样编译器就不会感到困惑。显然,我们会通过当前实例的转换版本,以便我们针对正确的重载:
base.BaseMethod()
this.BaseMethod()
这是受@Mike z关于语言规范的评论的启发,该语言规范如下:
在绑定时,
Extensions.ExtensionMethod((Base) this);
和base.ExtensionMethod()
形式的基本访问表达式的计算方式与base.I
和base[E]
一样,其中{{1}是构造发生的类或结构的基类。因此,((B)this).I
和((B)this)[E]
对应B
和base.I
,但base[E]
被视为基类的实例。
该规范字面上说this.I
将被调用为this[E]
。但是在我们的情况下,this
会抛出编译错误,而base.I
将完美无缺。
在文档或编译器中看起来有些不对劲,但这个结论应该由对此事有更深入了解的人绘制(分页Lippert博士)。
是的,我会说是的。它有点像C#规范中的黑洞:实际上一切都运行得很完美,但突然之间你必须跳过一些箍,因为编译器在这种情况下不知道在方法调用中注入当前实例。 / p>
事实上,intellisense也对这种情况感到困惑:
我们已经确定该呼叫永远不会起作用,但智能感知可能会这样。另请注意它如何使用PortableClassLibrary &#34;添加&#34; 在名称后面,表示将添加((B) this).I
指令。这是不可能的,因为当前的命名空间实际上是base.ExtensionMethod();
。但当然,当您实际添加该方法调用时:
并且一切都没有按预期工作。
主要结论很简单:如果支持扩展方法的这种利基使用,那就太好了。不实现它的主要理由是因为它会鼓励人们编写扩展方法而不是实例方法。
这里显而易见的问题当然是您可能无法始终访问基类,这使得扩展方法成为必需,但目前的实现却无法实现。
或者,正如我们所见,不太可能使用可爱的语法。