为什么在扩展类中调用扩展方法需要使用“this”关键字

我为ASP.NET MVC ViewPage创build了一个扩展方法,例如:

public static class ViewExtensions { public static string Method<T>(this ViewPage<T> page) where T : class { return "something"; } } 

从视图(从ViewPage )调用此方法时,我得到错误“ CS0103:名称'方法'不存在于当前的上下文中 ”,除非我用this关键字来调用它:

 <%: Method() %> <!-- gives error CS0103 --> <%: this.Method() %> <!-- works --> 

为什么需要this关键字? 还是没有它的工作,但我错过了什么?

(我认为这个问题一定有重复,但是我找不到)

更新

正如Ben Robinson所说 ,调用扩展方法的语法只是编译器糖。 那么为什么编译器不能自动检查当前types的基types的扩展方法,而不需要这个关键字?

几点:

首先,build议的function(在扩展方法调用上隐含“this”)是不必要的 。 对于LINQ查询parsing来说,扩展方法是必需的, 接收者总是在查询中声明,所以没有必要支持隐式这使得LINQ工作。

其次,这个特性更一般的扩展方法devise相反:即扩展方法允许扩展一个你自己不能扩展的types ,或者是因为它是一个接口,而你不知道实现,或者是因为你知道实现,但没有源代码。

如果您正在使用该types中的types的扩展方法,则可以访问源代码。 那么为什么你首先使用扩展方法呢? 如果您有权访问扩展types的源代码,则可以自己编写一个实例方法 ,然后根本不必使用扩展方法! 然后您的实现可以利用访问对象的私有状态,哪些扩展方法不能。

在你有权访问的types中使用扩展方法比较容易,鼓励使用扩展方法而不是实例方法。 扩展方法很好,但是如果你有一个实例方法,通常会更好。

考虑到这两点,语言devise者不再需要解释为什么该特征不存在。 现在解释你为什么应该这样做 。 function与他们有巨大的成本。 这个function是没有必要的,并且违背了扩展方法的devise目标。 我们为什么要承担执行成本呢? 解释这个function启用了什么引人注目的重要场景,我们将在未来考虑实施它。 我没有看到任何令人信服的重要的情况,但也许有一个我错过了。

没有它,编译器只是把它看作静态类中的一个静态方法,它把页面作为第一个参数。 即

 // without 'this' string s = ViewExtensions.Method(page); 

 // with 'this' string s = page.Method(); 

在实例方法中,'this'是透明地隐式传递给每个方法的,所以你可以访问它提供的所有成员。

扩展方法是静态的。 通过调用Method()而不是this.Method()Method(this) ,您不会告诉编译器传递给该方法的内容。

你可能会说'为什么不知道调用对象是什么,并把它作为parameter passing?

答案是扩展方法是静态的,可以从静态上下文中调用,没有“this”。

我想他们可以在编译期间检查,但说实话,这可能是很多工作的极less回报。 说实话,我没有看到一些明确的扩展方法调用的好处。 事实上,他们可以被误解为实例方法,这意味着它们有时可能相当不直观(例如,NullReferenceExceptions不会被抛出)。 我有时会认为他们应该引入一个新的“pipe道式”运算符来扩展方法。

需要注意的是,扩展方法和常规方法之间存在差异。 我想你刚刚遇到其中一个。

我将给你另一个区别的例子:在null对象引用上调用扩展方法相当简单。 幸运的是,这对于常规方法来说要困难得多。 (但是可以做到,IIRC,Jon Skeet演示了如何通过操纵CIL代码来实现这一点)。

 static void ExtensionMethod(this object obj) { ... } object nullObj = null; nullObj.ExtensionMethod(); // will succeed without a NullReferenceException! 

这就是说,我同意, this是需要调用扩展方法似乎有点不合逻辑。 毕竟,一个扩展方法应该是理想的“感觉”,并像一个正常的行为。

但实际上,扩展方法更像是在现有语言之上添加语法糖,而不是在各个方面都很好地融入语言的早期核心function。

因为ViewPage类不存在扩展方法。 你需要告诉编译器你在调用扩展方法。 记住this.Method()ViewExtensions.Method(this)编译器糖。 用同样的方法,你不能用方法名称在任何类中间调用扩展方法。

我正在研究一个stream畅的API,并遇到同样的问题。 即使我有权访问我正在扩展的类,我仍然希望每个stream利方法的逻辑都在它们自己的文件中。 这个“这个”关键字是非常不直观的,用户一直在想方法丢失。 我所做的是使我的类一个部分类,实现我需要的方法,而不是使用扩展方法。 我没有看到答案中的部分内容。 如果你有这个问题partials可能是一个更好的select。