Delen via


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:

  1. Moet een void returntype hebben.
  2. Kan geen out parameters hebben.
  3. 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 privateis:

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 een partial 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 van partial 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.