اگر به قالب ابتدایی یک برنامهی کنسول #C دقت کنیم، همواره به ساختار استاندارد زیر میرسیم:
در اینجا یک سری import، به همراه تعریف فضای نام، تعریف کلاس و تعریف متد Main وجود دارند ... تا بتوان یک سطر Hello World را در کنسول نمایش داد. در این حالت اگر تازه شروع به یادگیری زبان #C کرده باشید، مفاهیم زیادی را باید در جهت درک آن فرا بگیرید؛ برای مثال static چیست؟ args چیست؟ کاربرد فضای نام چیست و غیره. کاری که در C# 9.0 انجام شده، امکان حذف تمام این عوامل در جهت نمایش تک سطر Hello World است که به آن top level programs و یا top level statements گفته میشود.
تبدیل قالب پیشفرض برنامههای کنسول به یک Top level program
در C# 9.0 میتوان تمام سطرهای فوق را به دو سطر زیر تقلیل داد و خلاصه کرد:
این قطعه کد بدون هیچگونه مشکلی در C# 9.0 کامپایل میشود و به این ترتیب زبان #C را تبدیل و یا شبیه به یک «زبان اسکریپتی» ساده میکند.
روش استفاده از متدهای async در Top level programs
زمانیکه نقطهی آغازین برنامه را تبدیل به یک top level program کردیم، دیگر دسترسی مستقیمی را به متد Main نداریم تا آنرا async Task دار معرفی کنیم و پس از آن بتوانیم به سادگی با متدهای async کار کنیم. برای رفع این مشکل، کامپایلر فقط کافی است یک await را در قطعه کد شما پیدا کند. خودش به صورت خودکار متد Main غیرهمزمانی را جهت اجرای کدها، تشکیل میدهد. به همین جهت برای کار با کدهای async در اینجا، نیاز به تنظیم خاصی نیست و قطعه کد زیر که در آن متد MyMethodAsync را اجرا میکند، بدون مشکل کامپایل و اجرا خواهد شد:
روش دسترسی به args در Top level programs
همانطور که در قطعه کد ابتدایی این مطلب مشخص است، متد Main به همراه پارامتر string[] args نیز هست. اما اکنون در Top level programs که فاقد متد Main هستند، چگونه میتوان به این آرگومانهای ارسالی توسط کاربر دسترسی یافت؟
پاسخ: پارامتر args نیز هنوز در اینجا قابل دسترسی است؛ فقط به ظاهر مخفی است:
ارائهی return codes به فراخون در Top level programs
بعضی از برنامههای کنسول در انتهای متد Main خود برای مثال return 0 و یا return 1 را دارند؛ که اولی به معنای موفقیت عملیات و دومی به معنای شکست عملیات است. در top level programs نیز میتوان این returnها را در انتهای کار قید کرد:
که یک چنین خروجی نهایی را توسط کامپایلر تولید میکند:
امکان تعریف کلاسها و متدها در Top level programs
در تک فایل program.cs برنامه، در حین کار با Top level programs محدودیتی از لحاظ تعریف متدها، کلاسها و غیره نیست؛ یک مثال:
همانطور که مشاهده میکنید، در حالت کار اسکریپتی با زبان #C، امکان استفادهی از کلاسها و یا متدها نیز وجود دارد؛ اما با یک شرط: این تعاریف باید پس از Top-level statements قرار گیرند. یعنی اگر متد و کلاس تعریف شده را به بالای فایل انتقال دهید، به خطای کامپایلر زیر خواهید رسید:
سطوح دسترسی به کلاسها و متدهای تعریف شدهی در Top level programs
اگر قطعه کد مثال قبل را کامپایل کنیم، نمونهی دیکامپایل شدهی آن به صورت زیر است:
همانطور که مشاهده میکنید، کامپایلر نه فقط نام متدها را تغییر دادهاست، بلکه سطوح دسترسی به آنها را یا private و یا internal تعریف کردهاست. به این معنا که کلاسها و متدهای تعریف شدهی در Top level programs در سایر کتابخانهها و یا برنامهها، قابل استفاده و دسترسی نیستند. البته کلاس public class Greeter به همان صورت public باقی میماند و سطح دسترسی آن تغییری نمیکند.
نوع متدهای تعریف شدهی در Top level programs
مثال زیر را که یک top level program است، درنظر بگیرید:
متد AddToX که static نیست، امکان دسترسی به متغیر x را یافتهاست. با توجه به اینکه متد Main هم static است، چطور چنین چیزی ممکن شدهاست؟
پاسخ: متدهایی که در top level programs تعریف میشوند در حقیقت از نوع local functions هستند که در ابتدا در C# 7.0 معرفی شدند و سپس در C# 8.0 امکان تعریف نمونههای static آنها نیز میسر شد.
قطعه کد فوق در اصل به صورت زیر کامپایل میشود که متدهای AddToX و Foo در آن داخل متد Main تشکیل شده، به صورت local function تعریف شدهاند:
فقط یک local function از نوع static، دسترسی به متغیرهای تعریف شدهی در متد Main را ندارد.
using System; namespace CS9Features { class Program { static void Main(string[] args) { Console.WriteLine("Hello World!"); } } }
تبدیل قالب پیشفرض برنامههای کنسول به یک Top level program
در C# 9.0 میتوان تمام سطرهای فوق را به دو سطر زیر تقلیل داد و خلاصه کرد:
using System; Console.WriteLine("Hello World!");
روش استفاده از متدهای async در Top level programs
زمانیکه نقطهی آغازین برنامه را تبدیل به یک top level program کردیم، دیگر دسترسی مستقیمی را به متد Main نداریم تا آنرا async Task دار معرفی کنیم و پس از آن بتوانیم به سادگی با متدهای async کار کنیم. برای رفع این مشکل، کامپایلر فقط کافی است یک await را در قطعه کد شما پیدا کند. خودش به صورت خودکار متد Main غیرهمزمانی را جهت اجرای کدها، تشکیل میدهد. به همین جهت برای کار با کدهای async در اینجا، نیاز به تنظیم خاصی نیست و قطعه کد زیر که در آن متد MyMethodAsync را اجرا میکند، بدون مشکل کامپایل و اجرا خواهد شد:
using System; using System.Threading.Tasks; await MyMethodAsync(); Console.WriteLine("Hello World!"); static async Task MyMethodAsync() { await Task.Yield(); }
روش دسترسی به args در Top level programs
همانطور که در قطعه کد ابتدایی این مطلب مشخص است، متد Main به همراه پارامتر string[] args نیز هست. اما اکنون در Top level programs که فاقد متد Main هستند، چگونه میتوان به این آرگومانهای ارسالی توسط کاربر دسترسی یافت؟
پاسخ: پارامتر args نیز هنوز در اینجا قابل دسترسی است؛ فقط به ظاهر مخفی است:
using System; Console.WriteLine(args[0]);
بعضی از برنامههای کنسول در انتهای متد Main خود برای مثال return 0 و یا return 1 را دارند؛ که اولی به معنای موفقیت عملیات و دومی به معنای شکست عملیات است. در top level programs نیز میتوان این returnها را در انتهای کار قید کرد:
using System; Console.WriteLine($"Hello world!"); return 1;
// <Program>$ using System; using System.Runtime.CompilerServices; [CompilerGenerated] internal static class <Program>$ { private static int <Main>$(string[] args) { Console.WriteLine("Hello world!"); return 1; } }
امکان تعریف کلاسها و متدها در Top level programs
در تک فایل program.cs برنامه، در حین کار با Top level programs محدودیتی از لحاظ تعریف متدها، کلاسها و غیره نیست؛ یک مثال:
using System; var greeter = new Greeter(); var helloTeacher = greeter.Greet("teacher"); var helloStudents = SayHello("students"); Console.WriteLine(helloTeacher); Console.WriteLine(helloStudents); static string SayHello(string name) { return "Hello, " + name; } public class Greeter { public string Greet(string name) { return "Hello, " + name; } }
Top-level statements must precede namespace and type declarations. [CS9Features]csharp(CS8803)
سطوح دسترسی به کلاسها و متدهای تعریف شدهی در Top level programs
اگر قطعه کد مثال قبل را کامپایل کنیم، نمونهی دیکامپایل شدهی آن به صورت زیر است:
using System; using System.Runtime.CompilerServices; [CompilerGenerated] internal static class <Program>$ { private static void <Main>$(string[] args) { Greeter greeter = new Greeter(); string helloTeacher = greeter.Greet("teacher"); string helloStudents = SayHello("students"); Console.WriteLine(helloTeacher); Console.WriteLine(helloStudents); static string SayHello(string name) { return "Hello, " + name; } } }
نوع متدهای تعریف شدهی در Top level programs
مثال زیر را که یک top level program است، درنظر بگیرید:
using System; Foo(); var x = 3; int result = AddToX(4); Console.WriteLine(result); static void Foo() { Console.WriteLine("Foo"); } int AddToX(int y) { return x + y; }
پاسخ: متدهایی که در top level programs تعریف میشوند در حقیقت از نوع local functions هستند که در ابتدا در C# 7.0 معرفی شدند و سپس در C# 8.0 امکان تعریف نمونههای static آنها نیز میسر شد.
قطعه کد فوق در اصل به صورت زیر کامپایل میشود که متدهای AddToX و Foo در آن داخل متد Main تشکیل شده، به صورت local function تعریف شدهاند:
// <Program>$ using System; using System.Runtime.CompilerServices; [CompilerGenerated] internal static class <Program>$ { private static void <Main>$(string[] args) { Foo(); int x = 3; int result = AddToX(4); Console.WriteLine(result); int AddToX(int y) { return x + y; } static void Foo() { Console.WriteLine("Foo"); } } }