Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
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 fångas i de relevanta LDM-anteckningarna (Language Design Meeting) .
Du kan läsa mer om processen för att införa funktionsspecifikationer i C#-språkstandarden i artikeln om specifikationerna.
Champion-problem: https://github.com/dotnet/csharplang/issues/2765
Sammanfattning
Tillåt att en sekvens av -instruktioner förekommer precis före namespace_member_declarationi en compilation_unit (t.ex. källfil).
Semantiken är att om en sådan sekvens av -instruktioner finns, genereras följande typdeklaration, modulo det faktiska metodnamnet:
partial class Program
{
static async Task Main(string[] args)
{
// statements
}
}
Se även https://github.com/dotnet/csharplang/issues/3117.
Motivation
Det finns en viss mängd standardkod som omgärdar även de enklaste programmen, på grund av behovet av en explicit Main metod. Detta verkar komma i vägen för språkinlärning och program klarhet. Det primära målet med funktionen är därför att tillåta C#-program utan onödiga pannplattor runt dem, för elevernas skull och tydligheten i koden.
Detaljerad design
Syntax
Den enda ytterligare syntaxen är att tillåta en sekvens med -instruktions i en kompileringsenhet, precis före namespace_member_declarations:
compilation_unit
: extern_alias_directive* using_directive* global_attributes? statement* namespace_member_declaration*
;
Endast en kompilenhet tillåts ha -instruktions.
Exempel:
if (args.Length == 0
|| !int.TryParse(args[0], out int n)
|| n < 0) return;
Console.WriteLine(Fib(n).curr);
(int curr, int prev) Fib(int i)
{
if (i == 0) return (1, 0);
var (curr, prev) = Fib(i - 1);
return (curr + prev, curr);
}
Semantik
Om det finns några toppnivåinstruktioner i någon kompileringsenhet i programmet är innebörden som om de kombinerades i blocktexten i en Main metod för en Program-klass i det globala namnområdet enligt följande:
partial class Program
{
static async Task Main(string[] args)
{
// statements
}
}
Typen heter "Program", så den kan refereras med namn från källkoden. Det är en partiell typ, så en typ med namnet "Program" i källkoden måste också deklareras som partiell.
Men metodnamnet "Main" används endast för illustrationsändamål, det faktiska namnet som används av kompilatorn är implementeringsberoende och metoden kan inte refereras med namn från källkoden.
Metoden är avsedd som startpunkt för programmet. Uttryckligen deklarerade metoder som enligt konventionen kan betraktas som startpunktskandidater ignoreras. En varning rapporteras när detta inträffar. Det går att ange en annan startpunkt via -main:<type> kompilatorväxeln.
Startpunktsmetoden har alltid en formell parameter, string[] args. Körningsmiljön skapar och skickar ett string[] argument som innehåller de kommandoradsargument som angavs när programmet startades. Argumentet string[] är aldrig null, men det kan ha en längd på noll om inga kommandoradsargument har angetts. Parametern "args" finns i omfånget inom toppnivåinstruktioner och finns inte i omfånget utanför dem. Standardregler för namnkonflikter och skuggning gäller.
Asynkrona åtgärder tillåts i toppnivåinstruktioner i den grad de tillåts i instruktioner inom en vanlig asynkron startpunktsmetod. De krävs dock inte, om await uttryck och andra asynkrona åtgärder utelämnas genereras ingen varning.
Signaturen för den genererade inträdespunktsmetoden bestäms baserat på operationer som används av huvudnivåinstruktionerna på följande sätt:
| Async-operations\Return-with-expression | Närvarande | frånvarande |
|---|---|---|
| Närvarande | static Task<int> Main(string[] args) |
static Task Main(string[] args) |
| frånvarande | static int Main(string[] args) |
static void Main(string[] args) |
Exemplet ovan skulle ge följande $Main metoddeklaration:
partial class Program
{
static void $Main(string[] args)
{
if (args.Length == 0
|| !int.TryParse(args[0], out int n)
|| n < 0) return;
Console.WriteLine(Fib(n).curr);
(int curr, int prev) Fib(int i)
{
if (i == 0) return (1, 0);
var (curr, prev) = Fib(i - 1);
return (curr + prev, curr);
}
}
}
Samtidigt ett exempel som detta:
await System.Threading.Tasks.Task.Delay(1000);
System.Console.WriteLine("Hi!");
skulle ge:
partial class Program
{
static async Task $Main(string[] args)
{
await System.Threading.Tasks.Task.Delay(1000);
System.Console.WriteLine("Hi!");
}
}
Ett exempel som liknar detta:
await System.Threading.Tasks.Task.Delay(1000);
System.Console.WriteLine("Hi!");
return 0;
skulle ge:
partial class Program
{
static async Task<int> $Main(string[] args)
{
await System.Threading.Tasks.Task.Delay(1000);
System.Console.WriteLine("Hi!");
return 0;
}
}
Och ett exempel som detta:
System.Console.WriteLine("Hi!");
return 2;
skulle ge:
partial class Program
{
static int $Main(string[] args)
{
System.Console.WriteLine("Hi!");
return 2;
}
}
Omfång för lokala variabler på toppnivå och lokala funktioner
Även om lokala variabler och funktioner på den översta nivån är "omslutna" i den genererade startpunktsmetoden bör de fortfarande finnas i omfånget i hela programmet i varje kompileringsenhet. I syfte att utvärdera enkla namn när det globala namnområdet har nåtts:
- Först görs ett försök att utvärdera namnet inom den genererade startpunktsmetoden och endast om det här försöket misslyckas
- Den "vanliga" utvärderingen i den globala namnområdesdeklarationen utförs.
Detta kan leda till namnskugga av namnområden och typer som deklarerats inom det globala namnområdet samt till skuggning av importerade namn.
Om den enkla namnutvärderingen sker utanför toppnivåinstruktionerna och utvärderingen ger en lokal variabel eller funktion på den översta nivån, bör det leda till ett fel.
På så sätt skyddar vi vår framtida förmåga att bättre hantera "toppnivåfunktioner" (scenario 2 i https://github.com/dotnet/csharplang/issues/3117) och kan ge användbar diagnostik till användare som felaktigt tror att de stöds.
C# feature specifications