Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Notatka
Ten artykuł jest specyfikacją funkcji. Specyfikacja służy jako dokument projektowy dla funkcji. Zawiera proponowane zmiany specyfikacji wraz z informacjami wymaganymi podczas projektowania i opracowywania funkcji. Te artykuły są publikowane do momentu sfinalizowania proponowanych zmian specyfikacji i włączenia ich do obecnej specyfikacji ECMA.
Mogą wystąpić pewne rozbieżności między specyfikacją funkcji a ukończoną implementacją. Te różnice są przechwytywane w odpowiednich spotkania projektowego języka (LDM).
Więcej informacji na temat procesu wdrażania specyfikacji funkcji można znaleźć w standardzie języka C# w artykule dotyczącym specyfikacji .
Kwestia mistrzowska: https://github.com/dotnet/csharplang/issues/2765
Streszczenie
Zezwalaj na sekwencję instrukcji ,, aby mogły pojawić się bezpośrednio przed namespace_member_declarationw compilation_unit (tj. pliku źródłowym).
Semantyka polega na tym, że jeśli taka sekwencja instrukcji jest obecna, następująca deklaracja typu, z pominięciem rzeczywistej nazwy metody, zostanie wygenerowana:
partial class Program
{
static async Task Main(string[] args)
{
// statements
}
}
Zobacz również https://github.com/dotnet/csharplang/issues/3117.
Motywacja
Istnieje pewna ilość standardowego kodu nawet w najprostszych programach, ze względu na potrzebę jawnego zastosowania metody Main. Wydaje się, że to przeszkadza w nauce języka i przejrzystości programu. Podstawowym celem tej funkcji jest umożliwienie pisania programów w języku C# bez zbędnego kodu szablonowego, z myślą o uczniach i przejrzystości kodu.
Szczegółowy projekt
Składnia
Jedyną dodatkową składnią jest umożliwienie sekwencji instrukcji s w jednostce kompilacji tuż przed namespace_member_declarations:
compilation_unit
: extern_alias_directive* using_directive* global_attributes? statement* namespace_member_declaration*
;
Tylko jedna jednostka kompilacji może zawierać instrukcje s.
Przykład:
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);
}
Semantyka
Jeśli jakiekolwiek instrukcje najwyższego poziomu są obecne w dowolnej jednostce kompilacji programu, znaczenie jest tak, jakby zostały połączone w treści bloku metody Main klasy Program w globalnej przestrzeni nazw w następujący sposób:
partial class Program
{
static async Task Main(string[] args)
{
// statements
}
}
Typ ma nazwę "Program", więc można odwoływać się do nazwy z kodu źródłowego. Jest to typ częściowy, więc typ o nazwie "Program" w kodzie źródłowym musi być również zadeklarowany jako częściowy.
Jednak nazwa metody "Main" jest używana tylko do celów ilustracyjnych, rzeczywista nazwa używana przez kompilator jest zależna od implementacji i nie można odwoływać się do metody według nazwy z kodu źródłowego.
Metoda jest wyznaczona jako punkt wejścia programu. Jawnie zadeklarowane metody, które zgodnie z konwencją można traktować jako kandydatów do punktu wejścia, są ignorowane. W takim przypadku zostanie zgłoszone ostrzeżenie. Istnieje możliwość określenia innego punktu wejścia za pomocą przełącznika kompilatora -main:<type> .
Metoda punktu wejścia zawsze ma jeden parametr formalny, string[] args. Środowisko wykonywania tworzy i przekazuje argument string[] zawierający argumenty wiersza polecenia określone podczas uruchamiania aplikacji. Argument string[] nigdy nie ma wartości null, ale może mieć długość zero, jeśli nie określono argumentów wiersza polecenia. Parametr "args" znajduje się w zakresie w instrukcjach najwyższego poziomu i nie znajduje się poza nimi. Mają zastosowanie reguły regularnego konfliktu nazw/cieniowania.
Operacje asynchroniczne są dozwolone w instrukcjach na najwyższym poziomie w takim stopniu, w jakim są dozwolone w instrukcjach w ramach regularnej metody punktu wejścia asynchronicznego. Nie są one jednak wymagane, jeśli await wyrażenia i inne operacje asynchroniczne zostaną pominięte, żadne ostrzeżenie nie zostanie wygenerowane.
Podpis metody wygenerowanego punktu wejścia jest określany na podstawie operacji używanych przez instrukcje najwyższego poziomu w następujący sposób:
| Asynchroniczne-operacje\Zwróć-z-wyrażeniem | prezent | nieobecny |
|---|---|---|
| prezent | static Task<int> Main(string[] args) |
static Task Main(string[] args) |
| nieobecny | static int Main(string[] args) |
static void Main(string[] args) |
Powyższy przykład daje następującą deklarację metody $Main:
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);
}
}
}
W tym samym czasie przykład podobny do następującego:
await System.Threading.Tasks.Task.Delay(1000);
System.Console.WriteLine("Hi!");
dałoby:
partial class Program
{
static async Task $Main(string[] args)
{
await System.Threading.Tasks.Task.Delay(1000);
System.Console.WriteLine("Hi!");
}
}
Przykład podobny do następującego:
await System.Threading.Tasks.Task.Delay(1000);
System.Console.WriteLine("Hi!");
return 0;
dałoby:
partial class Program
{
static async Task<int> $Main(string[] args)
{
await System.Threading.Tasks.Task.Delay(1000);
System.Console.WriteLine("Hi!");
return 0;
}
}
I oto przykład:
System.Console.WriteLine("Hi!");
return 2;
dałoby:
partial class Program
{
static int $Main(string[] args)
{
System.Console.WriteLine("Hi!");
return 2;
}
}
Zakres zmiennych lokalnych najwyższego poziomu i funkcji lokalnych
Mimo że zmienne lokalne i funkcje najwyższego poziomu są "opakowane" w metodę wygenerowanego punktu wejścia, powinny one nadal znajdować się w widoczności w całym programie w każdej jednostce kompilacji. W celu oceny prostej nazwy po osiągnięciu globalnej przestrzeni nazw:
- Najpierw podjęto próbę oceny nazwy w wygenerowanej metodzie punktu wejścia i tylko wtedy, gdy ta próba zakończy się niepowodzeniem
- Wykonywana jest ocena "regularna" w globalnej deklaracji przestrzeni nazw.
Może to prowadzić do cieniowania nazw przestrzeni nazw i typów zadeklarowanych w globalnej przestrzeni nazw, a także do cieniowania importowanych nazw.
Jeśli ocena prostych nazw występuje poza instrukcjami najwyższego poziomu i skutkuje lokalną zmienną lub funkcją najwyższego poziomu, powinno to prowadzić do błędu.
W ten sposób chronimy naszą przyszłą zdolność do lepszego rozwiązywania problemów z "funkcjami najwyższego poziomu" (scenariusz 2 w https://github.com/dotnet/csharplang/issues/3117), i jesteśmy w stanie zapewnić przydatną diagnostykę użytkownikom, którzy błędnie uważają, że są one obsługiwane.
C# feature specifications