Het uitbreiden van gedeeltelijke methoden
Notitie
Dit artikel is een functiespecificatie. De specificatie fungeert als het ontwerpdocument voor de functie. Het bevat voorgestelde specificatiewijzigingen, samen met informatie die nodig is tijdens het ontwerp en de ontwikkeling van de functie. Deze artikelen worden gepubliceerd totdat de voorgestelde specificaties zijn voltooid en opgenomen in de huidige ECMA-specificatie.
Er kunnen enkele verschillen zijn tussen de functiespecificatie en de voltooide implementatie. Deze verschillen worden vastgelegd in de relevante notities van de Language Design Meeting (LDM).
Meer informatie over het proces voor het aannemen van functiespeclets in de C#-taalstandaard vindt u in het artikel over de specificaties.
Samenvatting
Dit voorstel is bedoeld om alle beperkingen rond de handtekeningen van partial
methoden in C# te verwijderen. Het doel is om de set scenario's uit te breiden waarin deze methoden kunnen werken met brongeneratoren, evenals een meer algemeen declaratieformulier voor C#-methoden.
Zie ook de oorspronkelijke specificatie van gedeeltelijke methoden (§15.6.9).
Motivatie
C# biedt beperkte ondersteuning voor ontwikkelaars die methoden splitsen in declaraties en definities/implementaties.
partial class C
{
// The declaration of C.M
partial void M(string message);
}
partial class C
{
// The definition of C.M
partial void M(string message) => Console.WriteLine(message);
}
Een gedrag van partial
methoden is dat wanneer de definitie afwezig is, de taal gewoon alle aanroepen naar de partial
methode zal wissen. In wezen gedraagt het zich als een aanroep naar een [Conditional]
-methode waarbij de voorwaarde als onwaar is beoordeeld.
partial class D
{
partial void M(string message);
void Example()
{
M(GetIt()); // Call to M and GetIt erased at compile time
}
string GetIt() => "Hello World";
}
De oorspronkelijke motivatie voor deze functie was het genereren van broncode in de vorm van door de ontwerper gegenereerde code. Gebruikers bewerkten voortdurend de gegenereerde code omdat ze een bepaald aspect van de gegenereerde code wilden koppelen. Met name onderdelen van het opstartproces van Windows Forms, nadat onderdelen zijn geïnitialiseerd.
Het bewerken van de gegenereerde code was foutgevoelig, omdat elke actie die de ontwerper dwingt de code opnieuw te genereren, ertoe leidt dat de bewerkingen door de gebruiker verloren gaan. De partial
-methode verminderde deze spanning omdat ontwerpers haken in de vorm van partial
-methoden konden implementeren.
Ontwerpers kunnen haken zoals partial void OnComponentInit()
verzenden en ontwikkelaars kunnen declaraties voor hen definiëren of ze niet definiëren. In beide gevallen zou de gegenereerde code worden gecompileerd en zouden ontwikkelaars die geïnteresseerd waren in het proces, indien nodig kunnen aansluiten.
Dit betekent wel dat gedeeltelijke methoden verschillende beperkingen hebben:
- Moet een
void
returntype hebben. - Kan geen
out
parameters hebben. - Kan geen toegankelijkheid hebben (impliciet
private
).
Deze beperkingen bestaan omdat de taal code moet kunnen verzenden wanneer de aanroepsite wordt gewist. Omdat ze kunnen worden gewist, is private
de enige mogelijke toegang omdat het lid niet kan worden weergegeven in assembly-metagegevens. Deze beperkingen dienen ook om de set scenario's te beperken waarin partial
methoden kunnen worden toegepast.
Het voorstel hier is om alle bestaande beperkingen rond partial
methoden te verwijderen. Laat ze eigenlijk beschikken over out
parameters, niet-lege retourwaardetypes of elke vorm van toegankelijkheid. Dergelijke partial
declaraties zouden dan de extra vereiste hebben dat er een definitie moet bestaan. Dat betekent dat de taal niet hoeft na te denken over de impact van het wissen van de oproepsites.
Met deze uitbreiding van de reeks generatorscenario's kunnen partial
methoden deelnemen, wat goed aansluit bij onze broncode-generatiefunctie. Een regex kan bijvoorbeeld worden gedefinieerd met behulp van het volgende patroon:
[RegexGenerated("(dog|cat|fish)")]
partial bool IsPetMatch(string input);
Dit geeft zowel de ontwikkelaar een eenvoudige declaratieve manier om voor generatoren te kiezen, als de generatoren een gemakkelijke set van declaraties om door te nemen in de broncode, om hun gegenereerde output te sturen.
Vergelijk dat met de moeilijkheid die een generator zou hebben om het volgende codefragment te koppelen.
var regex = new RegularExpression("(dog|cat|fish)");
if (regex.IsMatch(someInput))
{
}
Aangezien de compiler generatoren niet toestaat om code te wijzigen die dit patroon aan elkaar koppelen, is dit vrijwel onmogelijk voor generatoren. Ze moeten gebruik maken van reflectie in de IsMatch
-implementatie of gebruikers vragen hun aanroepplaatsen te wijzigen naar een nieuwe methode en de regex herstructureren om de letterlijke tekenreeks als argument door te geven. Het is behoorlijk rommelig.
Gedetailleerd ontwerp
De taal verandert zodat partial
methoden kunnen worden geannoteerd met een expliciete toegankelijkheidswijziging. Dit betekent dat ze kunnen worden gelabeld als private
, public
, enz ...
Wanneer een partial
-methode een expliciete toegankelijkheidsmodifier heeft, vereist de taal dat de verklaring een overeenkomende definitie heeft, zelfs wanneer de toegankelijkheid private
is:
partial class C
{
// Okay because no definition is required here
partial void M1();
// Okay because M2 has a definition
private partial void M2();
// Error: partial method M3 must have a definition
private partial void M3();
}
partial class C
{
private partial void M2() { }
}
Verder verwijdert de taal alle beperkingen voor wat kan worden weergegeven op een partial
methode die een expliciete toegankelijkheid heeft. Dergelijke declaraties kunnen niet-ongeldige retourtypen bevatten, out
parameters, extern
modifier, enzovoort... Deze handtekeningen hebben de volledige expressiviteit van de C#-taal.
partial class D
{
// Okay
internal partial bool TryParse(string s, out int i);
}
partial class D
{
internal partial bool TryParse(string s, out int i) { ... }
}
Hierdoor kunnen partial
methoden expliciet deelnemen aan overrides
en interface
implementaties:
interface IStudent
{
string GetName();
}
partial class C : IStudent
{
public virtual partial string GetName();
}
partial class C
{
public virtual partial string GetName() => "Jarde";
}
De compiler wijzigt de fout die wordt verzonden wanneer een partial
-methode een ongeldig element bevat om in wezen te zeggen:
Kan
ref
niet gebruiken voor eenpartial
methode die geen expliciete toegankelijkheid heeft
Dit helpt ontwikkelaars in de juiste richting te wijzen bij het gebruik van deze functie.
Beperkingen:
-
partial
declaraties met expliciete toegankelijkheid moeten een definitie hebben -
partial
declaraties en definitiehandtekeningen moeten overeenkomen met alle methode- en parametermodificaties. De enige aspecten die kunnen verschillen zijn parameternamen en kenmerklijsten (dit is niet nieuw, maar eerder een bestaande vereiste vanpartial
methoden).
Vragen
gedeeltelijk voor alle leden
Gezien het feit dat we partial
uitbreiden om vriendelijker te zijn voor brongeneratoren, moeten we het ook uitbreiden om voor alle klasseleden te werken? Bijvoorbeeld, als we partial
constructors, operators, enzovoort kunnen declareren...
resolutie Het idee is goed, maar op dit moment in het C# 9-schema proberen we onnodige functie te voorkomen. Wilt u het onmiddellijke probleem oplossen van het uitbreiden van de functie om te werken met moderne brongeneratoren.
Het uitbreiden van partial
ter ondersteuning van andere leden wordt in aanmerking genomen voor de C# 10-release. Het lijkt waarschijnlijk dat we deze extensie zullen overwegen. Dit blijft een actief voorstel, maar is nog niet geïmplementeerd.
Abstract gebruiken in plaats van gedeeltelijk
De crux van dit voorstel is in wezen ervoor te zorgen dat een verklaring een overeenkomstige definitie/uitvoering heeft. Gezien dat we abstract
moeten gebruiken omdat het al een taalwoord is waarmee de ontwikkelaar moet nadenken over een implementatie?
resolutie Er was hierover een gezonde discussie, maar uiteindelijk werd besloten het niet te doen. Ja, de vereisten zijn bekend, maar de concepten zijn aanzienlijk anders. Kan de ontwikkelaar gemakkelijk laten geloven dat ze virtuele gokkasten maakten terwijl ze dat niet deden.
C# feature specifications