Dela via


Utöka partiella metoder

Not

Den här artikeln är en funktionsspecifikation. Specifikationen fungerar som designdokument för funktionen. Den innehåller föreslagna specifikationsändringar, tillsammans med information som behövs under utformningen och utvecklingen av funktionen. Dessa artiklar publiceras tills de föreslagna specifikationsändringarna har slutförts och införlivats i den aktuella ECMA-specifikationen.

Det kan finnas vissa skillnader mellan funktionsspecifikationen och den slutförda implementeringen. Dessa skillnader samlas in i de relevanta anteckningarna från Language Design Meeting (LDM) .

Du kan läsa mer om processen för att införa funktionsspecifikationer i C#-språkstandarden i artikeln om specifikationerna.

Champion-nummer: https://github.com/dotnet/csharplang/issues/3301

Sammanfattning

Det här förslaget syftar till att ta bort alla begränsningar kring signaturer för partial metoder i C#. Målet är att utöka uppsättningen scenarier där dessa metoder kan fungera med källgeneratorer samt att vara ett mer allmänt deklarationsformulär för C#-metoder.

Se också den original- partiska metodspecifikationen (§15.6.9).

Motivation

C# har begränsat stöd för utvecklare som delar upp metoder i deklarationer och definitioner/implementeringar.

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);
}

Ett beteende för partial metoder är att när definitionen saknas raderar språket helt enkelt alla anrop till metoden partial. I princip fungerar det som ett anrop till en [Conditional]-metod där villkoret utvärderades till falskt.

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";
}

Den ursprungliga motivationen för den här funktionen var källgenerering i form av designergenererad kod. Användarna redigerade ständigt den genererade koden eftersom de ville koppla någon aspekt av den genererade koden. Framför allt delar av startprocessen för Windows Forms efter att komponenterna initierats.

Redigering av den genererade koden var felbenägen eftersom alla åtgärder som gjorde att designern återskapade koden skulle göra så att användarredigeringen raderas. Metoden partial funktionen lättade på spänningen eftersom den gjorde det möjligt för designerna att skapa krokar i form av partial-metoder.

Designers kan generera krokar som partial void OnComponentInit() och utvecklare kan definiera deklarationer för dem eller inte definiera dem. I båda fallen skulle den genererade koden kompilera och utvecklare som var intresserade av processen kunde koppla in efter behov.

Det innebär att partiella metoder har flera begränsningar:

  1. Måste ha en void returtyp.
  2. Det går inte att ha out som antal parametrar.
  3. Det går inte att ha någon åtkomst (implicit private).

Dessa begränsningar finns eftersom språket måste kunna generera kod när anropswebbplatsen raderas. Eftersom de kan raderas private är den enda möjliga tillgängligheten eftersom medlemmen inte kan exponeras i sammansättningsmetadata. Dessa begränsningar kan också användas för att begränsa uppsättningen scenarier där partial metoder kan tillämpas.

Förslaget här är att ta bort alla befintliga begränsningar kring partial metoder. Låt dem i princip ha out parametrar, icke-void returtyper eller någon typ av åtkomsträttighet. Sådana partial deklarationer skulle då ha det extra kravet att en definition måste finnas. Det innebär att språket inte behöver ta hänsyn till effekten av att radera samtalswebbplatserna.

Detta skulle utöka uppsättningen generatorscenarier som partial metoder kan delta i och därmed länka till vår funktion för källgeneratorer. Till exempel kan en regex definieras med hjälp av följande mönster:

[RegexGenerated("(dog|cat|fish)")]
partial bool IsPetMatch(string input);

Detta ger både utvecklaren ett enkelt deklarativt sätt att välja generatorer och ge generatorer en mycket enkel uppsättning deklarationer att titta igenom i källkoden för att driva sina genererade utdata.

Jämför det med svårigheten som en generator skulle ha att koppla upp följande kodfragment.

var regex = new RegularExpression("(dog|cat|fish)");
if (regex.IsMatch(someInput))
{

}

Med tanke på att kompilatorn inte tillåter generatorer att ändra kod skulle det vara praktiskt taget omöjligt för generatorer att ansluta det här mönstret. De skulle behöva använda reflektion i IsMatch-implementeringen, eller be användarna att ändra sina anropspunkter till en ny metod och omstrukturera regex för att ange strängliteralen som ett argument. Det är ganska rörigt.

Detaljerad design

Språket kommer att ändras så att partial-metoder kan annoteras med en explicit åtkomstmodifierare. Detta innebär att de kan märkas som private, public, etc ...

När en partial-metod har en explicit hjälpmedelsmodifierare kräver språket att deklarationen har en matchande definition även när tillgängligheten är private:

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() { }
}

Dessutom tar språket bort alla begränsningar för vad som kan visas på en partial-metod som har en explicit tillgänglighet. Sådana deklarationer kan innehålla icke-void returtyper, out parametrar, extern modifierare osv ... Dessa signaturer kommer att ha fullständig expressivitet för C#-språket.

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) { ... }
}

Detta gör det uttryckligen möjligt för partial metoder att delta i overrides och interface implementeringar:

interface IStudent
{
    string GetName();
}

partial class C : IStudent
{
    public virtual partial string GetName(); 
}

partial class C
{
    public virtual partial string GetName() => "Jarde";
}

Kompilatorn ändrar det fel som genereras när en partial-metod innehåller ett olagligt element för att i princip säga:

Det går inte att använda ref på en partial-metod som saknar explicit tillgänglighet

Detta hjälper utvecklare i rätt riktning när de använder den här funktionen.

Inskränkningar:

  • partial-deklarationer med explicit tillgänglighet måste ha en definition
  • partial-deklarationer och definitionssignaturer måste matcha på alla metod- och parametermodifierare. De enda aspekter som kan skilja sig åt är parameternamn och attributlistor (detta är inte nytt utan snarare ett befintligt krav på partial metoder).

Frågor

delvis för alla medlemmar

Med tanke på att vi expanderar partial för att vara mer vänliga mot källgeneratorer bör vi också utöka det till att fungera på alla klassmedlemmar? Till exempel bör vi kunna deklarera partial konstruktorer, operatörer osv .

Resolution Idén är sund, men just nu i C# 9-schemat försöker vi undvika onödiga funktionskryp. Vill du lösa det omedelbara problemet med att utöka funktionen så att den fungerar med moderna källgeneratorer.

Att utöka partial för att stödja andra medlemmar kommer att övervägas för C# 10-versionen. Det verkar troligt att vi kommer att överväga det här tillägget. Detta är fortfarande ett aktivt förslag, men det har ännu inte genomförts.

Använd abstrakt i stället för partiell

Kärnan i detta förslag är i huvudsak att se till att en förklaring har en motsvarande definition /implementering. Med tanke på att vi bör använda abstract eftersom det redan är ett språknyckelord som tvingar utvecklaren att tänka på att ha en implementering?

Resolution Det fanns en sund diskussion om detta, men så småningom beslutades det emot. Ja, kraven är bekanta, men begreppen skiljer sig avsevärt. Kan enkelt få utvecklaren att tro att de skapade virtuella slottar när de inte gjorde det.