Perché devo usare "this" per chiamare un metodo di estensione all'interno della classe estesa?
-
06-07-2019 - |
Domanda
Ho scritto dozzine di metodi di estensione e funzionano tutti come previsto. Ma questa è la prima volta che ho incontrato un metodo di estensione in questo contesto.
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);
}
}
Sembra abbastanza normale, giusto? Ma nei miei controller, non riesco ad accedere a questo metodo di estensione digitando. Devo invece aggiungere il prefisso con la parola chiave "this". Ad esempio:
// 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) ));
Molto strano. Forse è perché mi trovo nell'oggetto istanza che sto estendendo? Il "controller" istanza che è?
Certo, sono stato in grado di duplicarlo in una semplice app console:
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;
}
}
Quindi perché 'questo'. lavorare?
Soluzione
I metodi di estensione non fanno parte di " default " ricerca membri - devi utilizzare un'espressione del modulo Target.Method
prima di controllare i metodi di estensione. this.Foo ()
è conforme a tale requisito, quindi funziona.
Dalla sezione 7.5.5.2:
In una chiamata di metodo (§7.5.5.1) di una delle forme
expr . identifier ( ) expr . identifier ( args ) expr . identifier < typeargs > ( ) expr . identifier < typeargs > ( args ) if the normal processing of the
l'invocazione non trova applicabile metodi, viene effettuato un tentativo di elaborazione il costrutto come metodo di estensione invocazione.
Certamente tutto ciò che dice è "il compilatore sta seguendo le specifiche" piuttosto che il motivo per cui la specifica è stata scritta in quel modo ... Non so se sia un motivo specifico, sebbene il fatto che tu possa invocare entrambi i membri dell'istanza e i membri statici che usano solo Metodo ()
(invece di specificare un'istanza o un tipo) possono essere rilevanti.
Altri suggerimenti
Penso che sia a causa di come funzionano i metodi di estensione.
Quando scrivi Go (), il compilatore presuppone che Go sia un metodo nella classe corrente, che non lo è.
I metodi di estensione sono "collegati" a un'istanza e si specifica l'istanza utilizzando questa parola chiave.