Why do I have to use “this” to call an extension method from within the extended class?

StackOverflow https://stackoverflow.com/questions/1616294

  •  06-07-2019
  •  | 
  •  

Question

I've written dozens of extension methods and they all work as expected. But this is the first time I ran into using an extension method in this context.

public static class ControllerExtensions
{
    public static RedirectToRouteResult RedirectToAction<TController>(
        this Controller controller
        , Expression<Action<TController>> action
      ) where TController : Controller
    {
      RouteValueDictionary routeValuesFromExpression = 
        ExpressionHelper.GetRouteValuesFromExpression<TController>(action);

      return new RedirectToRouteResult(routeValuesFromExpression);
    }
}

Looks normal enough, right? But within my controllers, I cannot access this extension method by typing. Instead, I have to prefix it with the keyword "this". For example:

// This does not work, I get a compiler error because
// RedirectToAction has no overload for the generic.
//
return
  RedirectToAction<MembershipController>(
     c => c.RegisterSuccess(Server.UrlEncode(code) ));

// But, this does work?!?!
//
return
  this.RedirectToAction<MembershipController>(
     c => c.RegisterSuccess(Server.UrlEncode(code) ));

Very odd. Perhaps it is because I am within the instance object I am extending? The "controller" instance that is?

Sure enough, I was able to duplicate it in a simple console app:

class Program
{
    static void Main(string[] args)
    {
        var x = new TestClass();
        x.Go<String>();
    }
}

public class TestClass
{
    public void Go()
    {
    }

    public void NextMethod()
    {
        // compiler error.  :(
        Go<String>();

        // works!
        this.Go<String>();
    }
}

public static class TestExtension
{
    public static string Go<T>(this TestClass theClass)
    {
        return String.Empty;
    }
}

So why does 'this.' work?

Was it helpful?

Solution

Extension methods aren't part of the "default" lookup for members - you have to be using an expression of the form Target.Method before extension methods are checked. this.Foo() conforms to that requirement, so it works.

From section 7.5.5.2:

In a method invocation (§7.5.5.1) of one of the forms

expr . identifier ( )
expr . identifier ( args )
expr . identifier < typeargs > ( )
expr . identifier < typeargs > ( args ) if the normal processing of the

invocation finds no applicable methods, an attempt is made to process the construct as an extension method invocation.

Admittedly all that says is "the compiler is following the spec" rather than the reason why the spec was written that way... I don't know whether there is any specific reason, although the fact that you can invoke both instance members and static members using just Method() (instead of specifying either an instance or a type) may be relevant.

OTHER TIPS

I think is because of how extension methods works.

When you write Go(), the compiler assumes that Go is a method in the current class, which isn't.

Extension methods are 'attached' to a instance, and you specify the instance using the this keyword.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top