Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Hinweis
Dieser Artikel ist eine Featurespezifikation. Die Spezifikation dient als Designdokument für das Feature. Es enthält vorgeschlagene Spezifikationsänderungen sowie Informationen, die während des Entwurfs und der Entwicklung des Features erforderlich sind. Diese Artikel werden veröffentlicht, bis die vorgeschlagenen Spezifikationsänderungen abgeschlossen und in die aktuelle ECMA-Spezifikation aufgenommen werden.
Es kann einige Abweichungen zwischen der Featurespezifikation und der abgeschlossenen Implementierung geben. Diese Unterschiede werden in den relevanten Sprachentwurfsbesprechungen (LDM)-Notizen erfasst.
Weitere Informationen zum Einführen von Featurespezifikationen in den C#-Sprachstandard finden Sie im Artikel zu den Spezifikationen.
Champion Issue: https://github.com/dotnet/csharplang/issues/2765
Zusammenfassung
Zulassen, dass eine Abfolge von Anweisungen direkt vor den namespace_member_declarationeiner compilation_unit (d. h. Quelldatei) erfolgt.
Die Semantik besteht darin, dass, wenn eine solche Abfolge von Anweisungen vorhanden ist, die folgende Typdeklaration, modulo den tatsächlichen Methodennamen, ausgegeben wird:
partial class Program
{
static async Task Main(string[] args)
{
// statements
}
}
Siehe auch https://github.com/dotnet/csharplang/issues/3117.
Motivation
Es gibt eine gewisse Menge an Bausteinen, die sogar die einfachsten Programme umgeben, aufgrund der Notwendigkeit einer expliziten Main Methode. Dies scheint auf die Art und Weise des Sprachlernens und der Programmklarkeit zu kommen. Das Hauptziel des Features ist daher, C#-Programme ohne unnötigen Textbaustein um sie herum zuzulassen, um Lernenden und der Klarheit des Codes zu dienen.
Detailliertes Design
Syntax
Die einzige zusätzliche Syntax ermöglicht eine Abfolge von Anweisungssyntaxenin einer Kompilierungseinheit unmittelbar vor den namespace_member_declarations:
compilation_unit
: extern_alias_directive* using_directive* global_attributes? statement* namespace_member_declaration*
;
Nur ein compilation_unit darf Anweisungenhaben.
Beispiel:
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
Wenn alle Anweisungen auf oberster Ebene in einer Kompilierungseinheit des Programms vorhanden sind, bedeutet dies, dass sie im Blocktext einer MainProgram Klasse im globalen Namespace wie folgt kombiniert wurden:
partial class Program
{
static async Task Main(string[] args)
{
// statements
}
}
Der Typ heißt "Program", kann daher anhand des Namens aus dem Quellcode verwiesen werden. Es handelt sich um einen Teiltyp, daher muss auch ein Typ namens "Program" im Quellcode als Teil deklariert werden.
Der Methodenname "Main" wird jedoch nur zu Veranschaulichungszwecken verwendet, der tatsächliche Name, der vom Compiler verwendet wird, ist von der Implementierung abhängig, und auf die Methode kann nicht anhand des Namens aus dem Quellcode verwiesen werden.
Die Methode wird als Einstiegspunkt des Programms festgelegt. Explizit deklarierte Methoden, die nach Konvention als Einstiegspunktkandidaten betrachtet werden können, werden ignoriert. Wenn dies geschieht, wird eine Warnung gemeldet. Es ist möglich, einen anderen Einstiegspunkt über den -main:<type> Compilerschalter anzugeben.
Die Einstiegspunktmethode weist immer einen formalen Parameter auf. string[] args Die Ausführungsumgebung erstellt und übergibt ein string[] Argument, das die Befehlszeilenargumente enthält, die beim Starten der Anwendung angegeben wurden. Das string[] Argument ist nie null, aber es kann eine Länge von Null aufweisen, wenn keine Befehlszeilenargumente angegeben wurden. Der Parameter "args" befindet sich im Bereich innerhalb von Anweisungen der obersten Ebene und befindet sich nicht außerhalb davon. Reguläre Namenskonflikt-/Schattenregeln gelten.
Asynchrone Vorgänge sind in Anweisungen der obersten Ebene bis zum Grad zulässig, in Anweisungen innerhalb einer regulären asynchronen Einstiegspunktmethode zulässig sind. Sie sind jedoch nicht erforderlich, wenn await Ausdrücke und andere asynchrone Vorgänge weggelassen werden, wird keine Warnung erzeugt.
Die Signatur der generierten Einstiegspunktmethode wird basierend auf Vorgängen bestimmt, die von den Anweisungen der obersten Ebene wie folgt verwendet werden:
| Async-operations\Return-with-expression | Anwesend | Abwesend |
|---|---|---|
| Anwesend | static Task<int> Main(string[] args) |
static Task Main(string[] args) |
| Abwesend | static int Main(string[] args) |
static void Main(string[] args) |
Das obige Beispiel würde die folgende $Main Methodendeklaration liefern:
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);
}
}
}
Gleichzeitig ist ein Beispiel wie folgt dargestellt:
await System.Threading.Tasks.Task.Delay(1000);
System.Console.WriteLine("Hi!");
würde folgendes erzielen:
partial class Program
{
static async Task $Main(string[] args)
{
await System.Threading.Tasks.Task.Delay(1000);
System.Console.WriteLine("Hi!");
}
}
Ein Beispiel wie folgt:
await System.Threading.Tasks.Task.Delay(1000);
System.Console.WriteLine("Hi!");
return 0;
würde folgendes erzielen:
partial class Program
{
static async Task<int> $Main(string[] args)
{
await System.Threading.Tasks.Task.Delay(1000);
System.Console.WriteLine("Hi!");
return 0;
}
}
Und ein Beispiel wie folgt:
System.Console.WriteLine("Hi!");
return 2;
würde folgendes erzielen:
partial class Program
{
static int $Main(string[] args)
{
System.Console.WriteLine("Hi!");
return 2;
}
}
Bereich lokaler Variablen auf oberster Ebene und lokale Funktionen
Auch wenn lokale Variablen und Funktionen auf oberster Ebene in die generierte Einstiegspunktmethode "umschlossen" werden, sollten sie in jedem Kompilierungsgerät weiterhin im Gesamten Programm enthalten sein. Zum Zweck der Simple-Name-Auswertung, sobald der globale Namespace erreicht ist:
- Zunächst wird versucht, den Namen innerhalb der generierten Einstiegspunktmethode auszuwerten, und nur, wenn dieser Versuch fehlschlägt.
- Die "reguläre" Auswertung innerhalb der globalen Namespacedeklaration wird ausgeführt.
Dies kann dazu führen, dass Namen von Namespaces und Typen, die im globalen Namespace deklariert sind, sowie zu Schattenung importierter Namen verwendet werden.
Wenn die einfache Namensauswertung außerhalb der Anweisungen der obersten Ebene erfolgt und die Auswertung eine lokale Variable oder Funktion auf oberster Ebene liefert, sollte dies zu einem Fehler führen.
Auf diese Weise schützen wir unsere zukünftige Fähigkeit, "Funktionen der obersten Ebene" (Szenario 2 in https://github.com/dotnet/csharplang/issues/3117) besser zu adressieren und benutzern, die versehentlich glauben, dass sie unterstützt werden, hilfreiche Diagnosen zu geben.
C# feature specifications