考虑以下情况:
Consider this situation:
public class Base
{
public void BaseMethod()
{
}
}
public class Sub : Base
{
public void SubMethod()
{
}
}
public static class Extensions
{
public static void ExtensionMethod(this Base @base) { }
}
以下是这段代码的一些有趣断言:
- 我无法从
Base
或Sub
中使用ExtensionMethod()
调用扩展方法。
- 我无法从
Sub
中使用base.ExtensionMethod()
调用扩展方法。
- 我可以使用
Extensions.ExtensionMethod(this)
从Sub
和Base
调用扩展方法。
- 我可以使用
this.ExtensionMethod()
从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()
{
}
}
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)
重载的方法有三种:
- 调用
BaseMethod()
,这将形成子类和扩展方法之间的代理。
你可以使用BaseMethod()
、base.BaseMethod()
和this.BaseMethod()
,因为现在你只是处理一个普通实例方法,该方法又会调用扩展方法。这是一个相当可行的解决方案,因为你不会污染公共API,但也必须提供一个单独的方法来执行本应在上下文中可访问的操作。
您还可以使用基本的扩展方法编写方式,跳过语法糖,直接编写它将被编译为的形式。现在,您可以传递参数,以便编译器不会混淆。显然,我们将传递当前实例的转换版本,以便我们定位正确的重载:
Extensions.ExtensionMethod((Base) this)
- 使用与
base.ExtensionMethod()
相同的翻译
这是受@Mike z 的言论启发,他提到了语言规范中的以下内容:
绑定时,形如base.I
和base[E]
的基本访问表达式将被评估,就像它们写成((B)this).I
和((B)this)[E]
一样,其中B
是包含该结构的类或结构的基类。因此,base.I
和base[E]
对应于this.I
和this[E]
,只不过this
被视为基类的实例。
规范字面上说base.I
将被解释为((B) this).I
。但在我们的情况下,base.ExtensionMethod();
将抛出编译错误,而((Base) this).ExtensionMethod();
将完美工作。
看起来文档或编译器有问题,但要得出这个结论需要深入了解的人(请联系Lippert博士)。
这不令人困惑吗?
是的,我认为是的。它感觉像C#规范中的黑洞:实际上,几乎所有东西都能无缝工作,但在这种情况下,您必须跳过某些障碍,因为编译器不知道在此场景中注入当前实例到方法调用中。
事实上,智能感知对这种情况也很困惑:
![enter image description here](https://istack.dev59.com/eem5Q.webp)
我们已经确定那个调用永远不会起作用,但智能感知认为它可能会。还请注意它在名称后面添加了“using PortableClassLibrary”,表示将添加using
指令。这是不可能的,因为当前命名空间实际上是PortableClassLibrary
。但当您实际添加该方法调用时:
![enter image description here](https://istack.dev59.com/a4YuX.webp)
一切都没有按预期工作。
或许有一个结论?
主要结论很简单:如果支持扩展方法的这种利基用法,那将是好的。不实现它的主要论点是,它会鼓励人们编写扩展方法而不是实例方法。
显然,这里的明显问题是,您可能并不总是可以访问基类,这使得扩展方法成为必需品,但根据当前实现,这是不可能的。
或者,正如我们所看到的,无法使用可爱的语法。
int
类型,对吗? - Mark Shevchenkothis
而不是base
。属性是Key
而不是Name
。KeyValuePair
没有Value
的setter。该属性应返回类型为int
。 - Mike Zboray