Waarom is het sleutelwoord ‘this’ vereist om een ​​extensiemethode aan te roepen vanuit de extended class

Ik heb een extensiemethode gemaakt voor een ASP.NET MVC ViewPage, bijvoorbeeld:

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

Als ik deze methode aanroep vanuit een View (afkomstig van ViewPage), krijg ik de foutmelding “CS0103: The name ‘Method’ bestaat niet in de huidige context” tenzij ik het trefwoord thisgebruik om het te noemen:

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

Waarom is het zoekwoord thisvereist? Of werkt het zonder, maar ik mis iets?

(Ik denk dat er een duplicaat van deze vraag moet zijn, maar ik kon er geen vinden)

Bijwerken:

Als Ben Robinson zegt, de syntaxis om extensiemethoden aan te roepen is gewoon compilersuiker. Waarom kan de compiler dan niet automatisch de extensiemethoden van de basistypen van het huidige type controleren zonder het trefwoord this te vereisen?


Antwoord 1, autoriteit 100%

Een paar punten:

Ten eerste is de voorgestelde functie (impliciete “dit.” bij een aanroep van een extensiemethode) onnodig. Uitbreidingsmethoden waren nodig om LINQ-querybegrippen te laten werken zoals we wilden; de ontvanger wordt altijd vermeld in de query, dus het is niet nodig om dit impliciet te ondersteunen om LINQ te laten werken.

Ten tweede werkt de functie tegenhet meer algemene ontwerp van extensiemethoden: namelijk dat extensiemethoden u in staat stellen een type uit te breiden dat u niet zelf kunt uitbreiden, ofwel omdat het is een interface en je kent de implementatie niet, of omdat je de implementatie wel kent maar niet over de broncode beschikt.

Als u zich in het scenario bevindt waarin u een extensiemethode gebruikt voor een type binnen dat type, dan heeft u weltoegang tot de broncode. Waarom gebruik je dan in de eerste plaats een extensiemethode?Je kunt zelf een instancemethodeschrijven als je toegang hebt tot de broncode van het uitgebreide type, en dan hoeft helemaal geen extension-methode te gebruiken! Uw implementatie kan dan profiteren van toegang tot de privéstatus van het object, wat bij extensiemethoden niet mogelijk is.

Het gemakkelijker maken om extensiemethoden te gebruiken vanuit een type waartoe u toegang hebt, stimuleert het gebruik van extensiemethoden in plaats van instantiemethoden. Uitbreidingsmethoden zijn geweldig, maar het is meestal beter om een ​​instantiemethode te gebruiken als je die hebt.

Gezien deze twee punten, rust de last niet langer op de taalontwerper om uit te leggen waarom de functie nietbestaat. Het is nu aan jou om uit te leggen waarom het zou. Functies brengen enorme kosten met zich mee. Deze functie is niet nodig en gaat in tegen de genoemde ontwerpdoelen van uitbreidingsmethoden; waarom zouden we de kosten voor de uitvoering ervan op ons nemen? Leg uit welk aantrekkelijk, belangrijk scenario deze functie mogelijk maakt en we zullen overwegen om het in de toekomst te implementeren. Ik zie geen overtuigend, belangrijk scenario dat dit rechtvaardigt, maar misschien is er een die ik heb gemist.


Antwoord 2, autoriteit 17%

Zonder dit ziet de compiler het gewoon als een statische methode in een statische klasse die pagina als eerste parameter gebruikt. d.w.z.

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

vs.

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

Antwoord 3, autoriteit 10%

Bij instantiemethoden wordt ‘dit’ impliciet transparant doorgegeven aan elke methode, zodat u toegang hebt tot alle leden die het biedt.

Extensiemethoden zijn statisch. Door Method()aan te roepen in plaats van this.Method()of Method(this), vertel je de compiler niet waaraan hij moet doorgeven de methode.

Je zou kunnen zeggen ‘waarom realiseert het zich niet gewoon wat het aanroepende object is en geeft dat niet door als parameter?’

Het antwoord is dat extensiemethoden statisch zijn en kunnen worden aangeroepen vanuit een statische context, waar geen ‘dit’ is.

Ik denk dat ze dat tijdens het compileren zouden kunnen controleren, maar om eerlijk te zijn, het is waarschijnlijk veel werk voor extreem weinig uitbetaling. En om eerlijk te zijn, zie ik weinig voordeel in het wegnemen van een deel van de explicietheid van extensiemethode-aanroepen. Het feit dat ze kunnen worden aangezien voor bijvoorbeeld methoden, betekent dat ze soms behoorlijk onintuïtief kunnen zijn (NullReferenceExceptions worden bijvoorbeeld niet gegenereerd). Ik denk soms dat ze een nieuwe ‘pipe-forward’-stijl operator voor uitbreidingsmethoden hadden moeten introduceren.


Antwoord 4, autoriteit 7%

Het is belangrijk op te merken dat er verschillen zijn tussen extensiemethoden en reguliere methoden.Ik denk dat je er zojuist een bent tegengekomen.

Ik zal je een voorbeeld geven van een ander verschil: het is vrij eenvoudig om een ​​extensiemethode aan te roepen op een nullobjectreferentie. Gelukkig is dit veel moeilijker te doen met reguliere methoden. (Maar het kan worden gedaan. IIRC, Jon Skeet demonstreerde hoe dit te doen door de CIL-code te manipuleren.)

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

Dat gezegd hebbende, ben ik het ermee eens dat het een beetje onlogisch lijkt dat thisvereist is om de extensiemethode aan te roepen. Een extensiemethode zou immers idealiter net zo moeten “voelen” en zich gedragen als een normale.

Maar in werkelijkheid zijn extensiemethoden meer als syntactische suiker toegevoegd aan de bestaande taal dan als een vroege kernfunctie die in alle opzichten goed in de taal past.


Antwoord 5, autoriteit 2%

Omdat de extensiemethode niet bestaat bij de klasse ViewPage. U moet de compiler vertellen waar u de extensiemethode voor aanroept. Onthoud dat this.Method()gewoon compiler-suiker is voor ViewExtensions.Method(this). Het is op dezelfde manier dat je niet zomaar een extensiemethode in het midden van een klasse kunt aanroepen met de methodenaam.


Antwoord 6, autoriteit 2%

Ik werk aan een vloeiende API en kwam hetzelfde probleem tegen. Hoewel ik toegang heb tot de klasse die ik uitbreid, wilde ik toch dat de logica van elk van de vloeiende methoden in hun eigen bestanden zou staan. Het “this”-sleutelwoord was erg onintuïtief, gebruikers bleven denken dat de methode ontbrak. Wat ik deed, was van mijn klas een gedeeltelijke klas maken die de methoden implementeerde die ik nodig had in plaats van uitbreidingsmethoden te gebruiken. Ik zag geen melding van gedeeltelijke in de antwoorden. Als je deze vraag hebt, is gedeeltelijke misschien een betere optie.

Other episodes