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:
- Måste ha en
void
returtyp. - Det går inte att ha
out
som antal parametrar. - 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å enpartial
-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.
C# feature specifications