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

我已经为 ASP.NET MVC 视图创建了一个扩展方法,例如:

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

当从 View (从 ViewPage派生)调用此方法时,除非使用 this关键字调用它,否则会得到错误“ CS0103: “ Method”名称在当前上下文中不存在”:

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

为什么需要 this关键字? 或者没有它也能工作,但是我遗漏了什么?

(我想这个问题一定有重复的地方,但是我找不到)

更新 :

作为 本 · 罗宾逊说,调用扩展方法的语法只是编译器的代码。那么为什么编译器不能在不需要 this 关键字的情况下自动检查当前类型的基类型的 for 扩展方法呢?

14880 次浏览

Without it the compiler just sees it as a static method in a static class which takes page as it's first parameter. i.e.

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

vs.

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

Because the extension method does not exist with the ViewPage class. You need to tell the compiler what you are calling the extension method on. Remember this.Method() is just compiler sugar for ViewExtensions.Method(this). It is the same way you can't just call an extention method within the middle of any class by the method name.

It's important to note that there are differences between extension methods and regular methods. I think you've just come across one of them.

I'll give you an example of another difference: It's fairly easy to call an extension method on a null object reference. Fortunately, this is much more difficult to do with regular methods. (But it can be done. IIRC, Jon Skeet demonstrated how to do this by manipulating CIL code.)

static void ExtensionMethod(this object obj) { ... }


object nullObj = null;
nullObj.ExtensionMethod();  // will succeed without a NullReferenceException!

That being said, I agree that it seems a little unlogical that this is required to call the extension method. After all, an extension method should ideally "feel" and behave just like a normal one.

But in reality, extension methods are more like syntactic sugar added on top of the existing language than an early core feature that fits nicely into the language in all respects.

On instance methods, 'this' is implicitly passed to each method transparently, so you can access all the members it provides.

Extension methods are static. By calling Method() rather than this.Method() or Method(this), you're not telling the compiler what to pass to the method.

You might say 'why doesn't it just realise what the calling object is and pass that as a parameter?'

The answer is that extension methods are static and can be called from a static context, where there is no 'this'.

I guess they could check for that during compilation, but to be honest, it's probably a lot of work for extremely little payoff. And to be honest, I see little benefit in taking away some of the explicitness of extension method calls. The fact that they can be mistaken for instance methods means that they can be quite unintuitive at times (NullReferenceExceptions not being thrown for example). I sometimes think that they should have introduced a new 'pipe-forward' style operator for extension methods.

A couple points:

First off, the proposed feature (implicit "this." on an extension method call) is unnecessary. Extension methods were necessary for LINQ query comprehensions to work the way we wanted; the receiver is always stated in the query so it is not necessary to support implicit this to make LINQ work.

Second, the feature works against the more general design of extension methods: namely, that extension methods allow you to extend a type that you cannot extend yourself, either because it is an interface and you don't know the implementation, or because you do know the implementation but do not have the source code.

If you are in the scenario where you are using an extension method for a type within that type then you do have access to the source code. Why are you using an extension method in the first place then? You can write an instance method yourself if you have access to the source code of the extended type, and then you don't have to use an extension method at all! Your implementation can then take advantage of having access to the private state of the object, which extension methods cannot.

Making it easier to use extension methods from within a type that you have access to is encouraging the use of extension methods over instance methods. Extension methods are great, but it is usually better to use an instance method if you have one.

Given those two points, the burden no longer falls on the language designer to explain why the feature does not exist. It now falls on you to explain why it should. Features have enormous costs associated with them. This feature is not necessary and works against the stated design goals of extension methods; why should we take on the cost of implementing it? Explain what compelling, important scenario is enabled by this feature and we'll consider implementing it in the future. I don't see any compelling, important scenario that justifies it, but perhaps there is one that I've missed.

I am working on a fluent API and ran into the same issue. Even though I have access to the class I'm extending I still wanted the logic of each of the fluent methods to be in their own files. The "this" keyword was very unintuitive, users kept thinking the method was missing. What I did was make my class a partial class that implemented the methods I needed instead of using extension methods. I saw no mention of partials in the answers. If you have this question partials might be a better option.