C# 9.0: دستورات سطح-بالا. یا باید بگم: سلام، متد Main کجاست؟

C# 9.0، ویژگی‌هایی جدید از زبان را معرفی می‌کند، و با این نوشته، من یک سریِ کوچک برای دیدن برخی از ویژگی‌های جدید شروع می‌کنم. بیایید در این نوشته با دستورات سطح-بالا شروع کنیم.

زمانی که یک Console application جدید با #C شروع می‌کنید، مقدار زیادی کد تکراری (Boilerplate) دریافت می‌کنید. در زیر شما کد یک برنامه جدید با نام ThomasClaudiusHuber.ConsoleApp مشاهده می‌کنید.

اگر در #C تازه‌کار هستید، این برنامه شما را با بسیاری از مفاهیم روبرو خواهد کرد:

  • using directives
  • namespaces
  • blocks { }
  • classes
  • static methods
  • void return type
  • array parameters
  • Console.WriteLine statement

در نسخه‌های قبلی #C و namespace .NET اختیاری بود. همچنین پارامتر args متد Main اختیاری است. شما می‌توانید این برنامه را بدون بلوک namespace و پارامتر args بنویسید:

این به این معنی است که از لیست ما، شما می‌توانید namespaces و array paremeters را حذف کنید.

  • using directives
  • namespaces
  • blocks { }
  • classes
  • static methods
  • void return type
  • array parameters
  • Console.WriteLine statement

C# 9.0 که با 5 NET. منتشر شد، کلیه ی دستورات را به سطوح بالاتری می رساند که اصطلاحا به آن برنامه سطح-بالا می‌گویند. به این معنی است که شما می‌توانید دستورات را مستقیما در سطح بالای یک فایل بنویسید. نیازی به تعریف کلاس و یا متد ایستای Main نیست. کد زیر Hello World را در Console app که با 9 #C نوشته شده است را نشان می‌دهد:

حالا، وقتی به لیست ویژگی‌های که ما در اینجا داریم در مقایسه با Console app اولیه نگاه کنید، به شکل زیر به نظر می‌رسد. فقط using directives و Console.WriteLine statement باقی مانده است:

  • using directives
  • namespaces
  • blocks { }
  • classes
  • static methods
  • void return type
  • array parameters
  • Console.WriteLine statement

این امر روشن می‌کند که برنامه‌های ساده سطح-بالا راهی آسان برای شروع #C هستند. و همچنین مبتدی‌ها مجبور نیستند تمام ویژگی‌های مختلف را از ابتدا یاد بگیرند.

هنگامی که از 5 NET. برای پروژه خود استفاده می‌کنید، به صورت پیش‌فرض 9.0 #C به عنوان ورژن زبان می‌باشد. بنابراین با بررسی فایل csproj. و مطمئن شدن از مقدار TargetFramework روی net5.0، اطمینان حاصل کنید که پروژه بر روی 5 NET. اجرا می‌شود:

برای استفاده از 5 NET.، شما نیاز به SDK 5 NET. دارید. شما می‌توانید از اینجا دانلود و SDK 5 .NET را نصب کنید.

متد Main کجاست؟

سوال خوبیه. اگر در گذشته با NET. کار می‌کردید، شما یادگرفته اید که ابتدای هر برنامه NET. متد ایستای Main می‌باشد. اما در یک برنامه سطح-بالا، متد Main وجود ندارد، همانطور که ما فقط این کد را داریم:

پس، متد Main ما کجاست؟ بیایید با استفاده از یک ابزار کلاسیک موجود، Intermediate Language Disassembler یا به اختصار ILDASM، ببینیم کد چه چیزی تولید می‌کند. (گزینه‌های قدرتمندتر دیگری مانند ILSpy یا dnSpy وجود دارند، اما در اینجا ILDASM ترجیح داده شده است).

اگر 2019 Visual Studio را نصب کرده باشید، یک پوشه 2019 Visual Studio در منوی start پیدا می‌کنید. در آن پوشه، 2019 Command Prompt for Visual Studio وجود دارد.

در Developer Command Prompt، فقط ildasm بنویسید و enter را برای باز شدن Intermediate Language Disassembler فشار دهید، همانطور که در عکس زیر می‌توانید می‌بینید. توجه داشته باشید که این کار تنها در Developer Command Prompt قابلیت انجام دارد و نه در command prompt معمول، چون Developer Command Prompt مسیر را مطابق با آن تنظیم می‌کند، بنابراین ILDASM.exe پیدا می‌شود.

در Intermediate Language Disassembler، بیایید فایل Console app .dll که از ویژگی برنامه سطح-بالا 9 #C استفاده می‌کند را باز کنیم. در عکس زیر من فایل dll. را باز کرده ام. شما می‌توانید کلاس Program$ و متد ایستای Main$ که در پشت صحنه برای ما تولید شده است را مشاهده کنید. این بدان معنی است که هنوز یک متد ایستای Main وجود دارد، اما اگر یک برنامه سطح-بالا ایجاد کنید که شما متد ایستای Main را به صورت صریح در کدتان تعریف نکنید، به صورت خودکار تولید می‌کند.

وقتی که بر روی متد Main$ دوبار کلیک می‌کنید، می‌توانید کد زبان میانی را ببینید. می‌توانید آن را در عکس زیر ببینید. این کد به شما نشان می دهد که کد سطح-بالا دستور Console.WriteLine در واقع به متد Main$ تولید شده، اضافه می‌شود. به عبارت بهتر: تمام دستورات سطح-بالای شما به متد Main$ تولید شده، اضافه می‌شود.

آرگومان خط‌فرمان

هنگامی که شما به متد خودکار تولید شده Main$ در تصویر بالا نگاه می‌کنید، می‌توانید ببینید که متد Main$، آرگومان مرسوم []string برای خط فرمان را نیز دارد. به عنوان دستورات سطح-بالایی که در فایل یک برنامه سطح-بالا می‌نویسید، که در آن متد Main$ به صورت خودکار ایجاد می‌شود، همچنین می‌توانید به پارامتر []string دسترسی داشته باشید، که به طور مرسوم args نام دارد. بنابراین شما می‌توانید چیزی مانند این در یک برنامه‌ سطح-بالا بنویسید تا آرگومان‌های خط‌فرمان را در کنسول چاپ کنید:

تنها و فقط یک فایل با دستورات سطح-بالا می‌تواند وجود داشته باشد.

بله، تنها یک متد Main در برنامه‌های NET. می‌تواند وجود داشته باشد. اگر چندین متد Main تعریف کنید، شما با خطای کامپایل مواجه می‌شوید تا زمانی که شما با پرچم main/ نقطه ورود را مشخص کنید.

برای یک فایل با دستورات سطح-بالا، متد Main$ تولید شده به عنوان نقطه ورود تنظیم می‌شود. چون تنها فقط یک نقطه ورود برای برنامه NET. وجود دارد، دقیقا فقط یک فایل با دستورات سطح-بالا می‌تواند وجود داشته باشد. بیایید این را آزمایش کنیم: در Console app، یک فایل Customer.cs اضافه کرده‌ام، علاوه بر فایل Program.cs موجود، و تلاش کردم تا از دستورات سطح-بالا به خوبی استفاده کنم. در عکس زیر می‌توانید ببینید که من خطای «فقط یک واحد کامپایل می‌تواند دستورات سطح-بالا داشته باشد» برخورد کرده ام که بدان معنی است که شما از دستورات سطح-بالا فقط در یک فایل از پروژه‌تان می توانید استفاده نمایید.


استفاده از Typeها و Namespaceها

زمانی که شما namespaceها و/یا typeها را در فایل سطح-بالای برنامه تعریف می‌کنید، این تعاریف باید بعد از تمام دستورات سطح-بالا باشند، در غیر این صورت شما با خطای کامپایل مواجه می‌شوید. برای مثال، به فایل زیر نگاه کنید. Friend type قبل از دستورات سطح-بالا تعریف شده است که منجر به خطا در اولین دستور می‌شود:

ترتیب صحیح برای آن برنامه مانند کد زیر می‌باشد. همانطور که می‌بینید، کلاس Friend بعد از دو دستور سطح-بالا برای ایجاد Friend و چاپ firstName از Friend در کنسول می‌باشد:

وقتی به کد تولید شده زبان میانی در ILDASM برای تکه کد برنامه بالا نگاه می‌کنیم، می‌توانید ببینید که type Friend به صورت یک فایل جدا ساخته شده است (همچنین می‌توانید کلاس Friend را در یک بلوک namespace قرار دهید). به این معنی است که کامپایلر #C دستورات سطح-بالا را که ابتدا باید تعریف کنید را می‌گیرد، و به متد Main$ تولید شده انتقال می‌دهد و سپس تمام typeها در آن فایل سطح-بالا را همانطور که در هر فایل دیگر #C کامپایل می‌کند، کامپایل می‌کند.

خلاصه

من فکر می‌کنم برنامه‌های سطح-بالا افزونه ای مطلوب برای #C هستند. بازی کردن با زبان را بسیار آسان می‌کند. و به ویژه برای پروژه‌های ساده که namespace اضافی، کلاس و متد Main را در کد خود ندارید، بسیار عالی است. NET. به صورت خودکار تمام کد‌های تکراری را برای شما در پشت صحنه‌ها تولید می‌کند، که این عالی است. این ویژگی را دوست دارم.

من فکر می‌کنم عیب این کار این است که مبتدیان ممکن است واقعا درک نکنند که در پشت صحنه چه اتفاقی می‌افتد. اما آیا برای شروع با #C لازم است؟ اگر شما فقط یک فایل در پروژه‌تان دارید شاید اینگونه نباشد. اما اگر #C را به یک توسعه دهنده جدید توضیح بدهم، از متد Main شروع می‌کنم، و می‌گویم این تقطه ورود به برنامه است. این چیزی است که همه درک می‌کنند. برنامه شما دقیقا همین جا شروع می‌شود! در صورتی که فایل سطح-بالا باشد، باید بگویم که فایل با دستورات سطح-بالا نقطه ورود به برنامه می‌باشد. و حالا، چه چیزی برای یک مبتدی ساده‌تر است:

الف) پیدا کردن فایل با دستورات سطح-بالا

ب) پیدا کردن متد Main

خب، اگر فقط یک فایل وجود داشته باشد، آسان است و خیلی هم مهم نیست، اما اگر چند فایل و بدون دانستن مفهوم namespace‌ها، کلاس‌ها و متدها سخت است بدانیم یک عبارت سطح-بالا واقعا چیست. بنابراین من فکر می‌کنم قطعا گزینه ب است. پیدا کردن متد Main آسان تر از پیدا کردن یک فایل با دستورات سطح-بالا می‌باشد.

بنابراین، من فکر می‌کنم دستورات سطح-بالا برای بازی با زبان در یک فایل خوب هستند، و آن‌ها برای برنامه‌های ساده که در آن فایل‌های زیادی نیاز ندارید عالی هستند. اما اگر شما در نظر دارید که می‌خواهید توسعه دهنده NET. شوید، به شما توصیه می‌کنم از جایی شروع کنید که همه چیز شروع می‌شود: متد Main. :)

امیدوارم این نوشته به شما کمک کند تا دستورات جدید سطح-بالا که از ویژگی‌های 9 #C است را بهتر درک کنید. در نوشته بعدی شما درباره خصوصیت init-only یاد خواهید گرفت.




Source: Thomas Claudius Huber - C# 9.0: Top-level Statements. Or Should I Say: Hey, Where’s the Main Method?

از آقای سیاوش دماری برای کمک و بررسی ترجمه این مقاله تشکر می‌کنم.


مقالات بیشتر در دات نت زوم

https://t.me/DotNetZoom