Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Jegyzet
Ez a cikk egy funkcióspecifikáció. A specifikáció a funkció tervezési dokumentumaként szolgál. Tartalmazza a specifikáció javasolt módosításait, valamint a funkció tervezése és fejlesztése során szükséges információkat. Ezeket a cikkeket mindaddig közzéteszik, amíg a javasolt specifikációmódosításokat nem véglegesítik, és be nem építik a jelenlegi ECMA-specifikációba.
A szolgáltatás specifikációja és a befejezett implementáció között eltérések lehetnek. Ezeket a különbségeket a vonatkozó nyelvi tervezési értekezlet (LDM) megjegyzései rögzítik.
A funkcióspektusok C# nyelvi szabványba való bevezetésének folyamatáról a specifikációkcímű cikkben olvashat bővebben.
Bajnok kérdés: https://github.com/dotnet/csharplang/issues/2765
Összefoglalás
Tegyük lehetővé, hogy egy utasítások sorozata közvetlenül egy namespace_member_declarations compilation_unit (például forrásfájl) előtt forduljon elő.
A lényege az, hogy ha utasítások ilyen sorozata megjelenik, a következő típusdeklaráció lenne kibocsátva, modulo a tényleges metódus neve:
partial class Program
{
static async Task Main(string[] args)
{
// statements
}
}
Lásd még: https://github.com/dotnet/csharplang/issues/3117.
Motiváció
Van egy bizonyos mennyiségű sablon szöveg még a legegyszerűbb programok körül, mert szükséges egy explicit Main módszer. Ez akadályozza a nyelvtanulást és a program átláthatóságát. A funkció elsődleges célja tehát az, hogy a C# programok körül felesleges sablonkód nélkül legyenek, a tanulók érdekében és a kód tisztaságának biztosítása céljából.
Részletes kialakítás
Szintaxis
Az egyetlen további szintaxis az, hogy engedélyezzük a utasításoksorozatát egy fordítási egységben, közvetlenül a namespace_member_declarationelőtt.
compilation_unit
: extern_alias_directive* using_directive* global_attributes? statement* namespace_member_declaration*
;
Csak egy compilation_unit rendelkezhet utasításokkal.
Példa:
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);
}
Szemantika
Ha bármelyik legfelső szintű utasítás megtalálható a program bármely fordítási egységében, a jelentése olyan, mintha a globális névtérben lévő Main osztály Program metódusának blokktörzsében lennének egyesítve, az alábbiak szerint:
partial class Program
{
static async Task Main(string[] args)
{
// statements
}
}
A típus neve "Program", ezért a forráskódból származó név alapján lehet hivatkozni. Részleges típus, ezért a forráskódban a "Program" nevű típust is részlegesként kell deklarálni.
A "Main" metódusnév azonban csak illusztrációs célokra használható, a fordító által használt tényleges név implementációfüggő, és a metódus nem hivatkozható a forráskódból származó név alapján.
A metódus a program belépési pontjaként van kijelölve. A kifejezetten deklarált módszereket, amelyek konvenció szerint belépési pont jelöltjeinek tekinthetők, figyelmen kívül hagyják. A rendszer figyelmeztetést ad ki, amikor ez történik. A fordítókapcsolóval -main:<type> másik belépési pontot is megadhat.
A belépési pont metódusának mindig van egy formális paramétere, string[] args. A végrehajtási környezet létrehoz és átad egy string[] argumentumot, amely tartalmazza az alkalmazás indításakor megadott parancssori argumentumokat. A string[] argumentum soha nem null értékű, de lehet, hogy nulla hosszúságú, ha nem adott meg parancssori argumentumokat. Az "args" paraméter a legfelső szintű utasítások hatókörében található, és nem tartozik rajtuk kívülre. A normál névütközési/árnyékolási szabályok érvényesek.
Az aszinkron műveletek a felső szintű utasításokban engedélyezettek olyan mértékben, hogy a normál aszinkron belépésipont-metódusban lévő utasításokban engedélyezettek legyenek. Ezekre azonban nincs szükség, ha await kifejezések és egyéb aszinkron műveletek kimaradnak, a rendszer nem hoz létre figyelmeztetést.
A létrehozott belépési pont metódus aláírását a legfelső szintű utasítások által használt műveletek alapján határozzuk meg az alábbiak szerint:
| Aszinkron-műveletek\Visszatérés kifejezéssel | Bemutató | Hiányzó |
|---|---|---|
| Bemutató | static Task<int> Main(string[] args) |
static Task Main(string[] args) |
| Hiányzó | static int Main(string[] args) |
static void Main(string[] args) |
A fenti példa a következő $Main metódusdeklarációt eredményezné:
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);
}
}
}
Ugyanakkor egy ilyen példa:
await System.Threading.Tasks.Task.Delay(1000);
System.Console.WriteLine("Hi!");
hozam:
partial class Program
{
static async Task $Main(string[] args)
{
await System.Threading.Tasks.Task.Delay(1000);
System.Console.WriteLine("Hi!");
}
}
Egy ilyen példa:
await System.Threading.Tasks.Task.Delay(1000);
System.Console.WriteLine("Hi!");
return 0;
hozam:
partial class Program
{
static async Task<int> $Main(string[] args)
{
await System.Threading.Tasks.Task.Delay(1000);
System.Console.WriteLine("Hi!");
return 0;
}
}
És egy ilyen példa:
System.Console.WriteLine("Hi!");
return 2;
hozam:
partial class Program
{
static int $Main(string[] args)
{
System.Console.WriteLine("Hi!");
return 2;
}
}
Felső szintű helyi változók és helyi függvények hatóköre
Annak ellenére, hogy a legfelső szintű helyi változók és függvények "be vannak csomagolva" a létrehozott belépési pont metódusába, a teljes program hatókörében kell lenniük minden fordítási egységben. Az egyszerű név kiértékelése céljából a globális névtér elérése után:
- Először megkísérli kiértékelni a nevet a létrehozott belépési pont metódusán belül, és csak akkor, ha ez a kísérlet meghiúsul
- A globális névtér-deklaráción belül a "reguláris" kiértékelés történik.
Ez a globális névtérben deklarált névterek és típusok névárnyékolásához, valamint az importált nevek árnyékolásához vezethet.
Ha az egyszerű névkiértékelés a legfelső szintű utasításokon kívül történik, és az értékelés egy legfelső szintű helyi változót vagy függvényt eredményez, amely hibát okozhat.
Ily módon megvédjük a jövőbeli képességünket a "Legfelső szintű függvények" jobb kezeléséhez (https://github.com/dotnet/csharplang/issues/31172. forgatókönyv), és hasznos diagnosztikát tudunk adni azoknak a felhasználóknak, akik tévesen úgy vélik, hogy támogatottak.
C# feature specifications