مقدمه

فصل جاری و فصل بعد، بنیادی‌ترین فصول این دوره هستند و شامل معرفی ساختارها و بررسی مفاهیمی هستند که در هر زبان برنامه‌نویسی وجود دارند و ما در اینجا این مفاهیم را در دنیای C# بررسی می‌کنیم. در اولین درس قصد داریم با بررسی آناتومی یک برنامه‌ی ساده‌ی C# با فرم کلی اپلیکیشن‌های C# آشنا شویم و تعدادی از مفاهیم گرامری این زبان را در عمل ببینیم. این مفاهیم در درس‌های بعدی با جزئیات بیشتر بررسی خواهند شد. البته قبل از شروع بررسی آناتومی یک برنامه‌ی C#، ابتدا با چند ویژگی که اسنتایل جدیدی به کدهای C# داده‌اند، آشنا می‌شویم.

استایل جدید برنامه‌های C#

در ورژن‌های 9 و 10 زبان C# چند ویژگی جدید برای ساده‌سازی و مختصرنویسی کدها معرفی شد. سه مورد از مهمترین این ویژگی‌ها که در درس مفاهیم اسمبلی و فضای نام در .NET هم از آنها نام بردیم، عبارتند از:

  • ویژگی Top-level statements که باعث می‌شود Entry point برنامه به صورت ضمنی توسط کامپایلر ایجاد شود و بنابراین، متد Main() در برنامه وجود نداشته باشد.
  • ویژگی Implicit using directives که بسته به نوع پروژه، تعدادی عبارت using را به طور ضمنی به پروژه اضافه می‌کند.
  • ویژگی Global using directives که به ما امکان می‌دهد عبارات using را در سطح پروژه (و نه فایل) تعریف کنیم.

ویژگی Top-level statements

در نسخه‌های قبل از C# 9 نقطه‌ی ورود یا Entry point برنامه‌های اجرایی C# یک متد با نام Main() بود که باید حتماً در پروژه ایجاد می‌شد و کدهایی که باید در نهایت اجرا می‌شدند، درون این متد نوشته می‌شد. اما با معرفی ویژگی Top-level statements در C# 9 این امکان فراهم شد تا Entry point برنامه به صورت ضمنی توسط کامپایلر ایجاد شود و نیازی به تعریف متد Main() نباشد. کد زیر مثالی است که در درس قبلی با استفاده از استایل قدیمی، یعنی بدون استفاده از Top-level statements نوشتیم.

Copy Icon Program.cs
using System;

class Program
{
  static void Main(string[] args)
  {
    Console.WriteLine("Hello, World!");
  }
}

اما با تکیه بر ویژگی Top-level statements می‌توانیم این کد را به صورت زیر خلاصه کنیم.

Copy Icon Program.cs
using System;

Console.WriteLine("Hello, World!")

همانطور که می‌بینید، دیگر نیازی به تعریف یک کلاس مثل Program و متد Main() و نوشتن کدها درون آن نیست. ما گزاره‌های (statements) اجرایی را بیرون از هر کلاس و متدی می‌نویسیم و این گزاره‌های Top-level توسط کامپایلر به یک متد که به صورت ضمنی ایجاد شده و ما حتی نامش را هم نمی‌دانیم (و نیازی هم به آن نداریم) منتقل شده و این متد به عنوان Entry point برنامه در نظر گرفته می‌شود.

ما می‌توانیم از این ویژگی استفاده کنیم یا نکنیم و الزامی در این مورد وجود ندارد. اگر به نوشتن کدهای اجرایی درون متد Main() عادت دارید، می‌توانید از همان استایل قدیمی استفاده کنید.

ویژگی Implicit using directives

در C# 10 یک ویژگی با نام Implicit using directive معرفی شد که به طور ضمنی چند عبارت using پرکاربرد را به پروژه اضافه می‌کند تا نیازی به وارد کردن آنها به صورت دستی نباشد. اینکه کدام عبارات using به پروژه اضافه شوند، به نوع پروژه بستگی دارد. مثلاٍ در پروژه‌های کنسول، عبارات using زیر به صورت ضمنی به پروژه اضافه می‌شوند.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

در نسخه‌های اخیر C# این ویژگی به صورت پیش‌فرض فعال است و بنابراین، می‌توانیم در مثال قبل، گزاره‌ی using system; را از بالای فایل حذف کنیم.

Copy Icon Program.cs
Console.WriteLine("Hello, World!");

اگر بخواهیم این ویژگی را غیرفعال کنیم، باید در فایل پروژه، عبارت enable زیر را به disable تبدیل کنیم.

Copy Icon SimpleCsharpApp.csproj
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
</Project>

ویژگی Global using directives

ویژگی دیگری که در C# 10 معرفی شد، ویزگی Global using directives است که به ما امکان می‌دهد با اضافه کردن کلمه کلیدی global به ابتدای یک عبارت using ترتیبی دهیم که آن فضای نام برای کل پروژه در دسترس باشد. برای مثال، اگر عبارات زیر را در یکی از فایل‌های پروژه وارد کنیم، این دو فضای نام برای سایر فایل‌های پروژه هم در دسترس خواهند بود.

Copy Icon Program.cs
global using System;
global using System.Collections.Generic;

آناتومی یک اپلیکیشن C#

در فصل قبل، دیدیم که چطور می‌توانیم از Visual Studio و Visual Studio Code برای ساخت اپ‌های .NET استفاده کنیم. مایکروسافت به جز این دو IDE یک گزینه‌ی دیگر را هم برای توسعه‌ی اپ‌های .NET به ما پیشنهاد می‌دهد و آن GitHub Codespaces است که در واقع، ورژن تحت وب vscode است. علاوه بر این ابزارهای مایکروسافتی، گزینه‌های دیگری مثل JetBrains Rider هم قابل انتخاب هستند. برای ادامه‌ی این دوره، ما از vscode استفاده می‌کنیم که روی پلتفرم‌های ویندوز، مک و لینوکس قابل استفاه است و رایگان و متن‌باز هم هست.

قصد داریم یک اپلیکیشن ساده ایجاد کرده و با بررسی بخش‌های مختلف آن، اطلاعاتی در مورد کلیات اپلیکیشن‌های C# به دست آوریم. پس vscode را اجرا کنید و از منوی File گزینه‌ی Open Folder را انتخاب کنید و سپس در کادری که باز می‌شود، به محل دلخواهتان بروید و یک دایرکتوری با نام SimpleCsharpApp ایجاد کنید. سپس، این دایرکتوری را انتخاب کرده و روی گزینه‌ی Select Folder کلیک کنید تا در محیط vscode باز شود. البته که این دایرکتوری فعلاً خالی است اما می‌توانید در پنل سمت چپ ببینید که این دایرکتوری در vscode باز شده است. حالا اگر از منوی View گزینه‌ی Terminal را انتخاب کنید، پنل ترمینال در vscode باز شده و می‌توانید ببینید که دایرکتوری جاری، همین دایرکتوری SimpleCsharpApp است. این موضوع را می‌توانید با استفاده از کامند pwd هم تست کنید.

بعد از اینکه مطمئن شدید که دایرکتوری جاری در ترمینال، دایرکتوری SimpleCsharpApp است، کامند زیر را اجرا کنید.

$ dotnet new console --framework net8.0

از فصل قبل یادآوری می‌کنم که این کامند باعث ایجاد یک پروژه‌ی همنام با دایرکتوری جاری می‌شود. البته این بار آپشن --use-program-main را به کار نبرده‌ایم تا پروژه با تکیه بر قابلیت Top-level statements و نتیجتاً بدون کلاس Program و متد Main() ایجاد شود.

به این ترتیب، یک دایرکتوری به نام obj (که فعلاً کاری به محتویاتش نداریم) به همراه یک فایل همنام با پروژه و دارای پسوند .csproj (که فایل پروژه نامیده می‌شود) و یک فایل کد با نام Program.cs در دایرکتوری پروژه ایجاد می‌شود. وقتی روی فایل Program.cs کلیک کنید، محتویاتش نمایش داده می‌شود و در ضمن، یک فایل دیگر که دارای پسوند .sln است (و فایل سولوشن نامیده می‌شود) و یک دایرکتوری دیگر با نام bin به دایرکتوری پروژه اضافه می‌شوند.

محتویات فایل Program.cs را پاک کنید و کد زیر را در آن وارد کنید.

Copy Icon Program.cs
int x = 12 * 30;
Console.WriteLine(x);

این برنامه‌ی ساده از دو گزاره (statement) تشکیل شده است. گزاره‌ی اول حاصلضرب 12 و 30 را در یک متغیر از نوع int با نام x ذخیره می‌کند و گزاره‌ی دوم، مقدار این متغیر را با استفاده از یک متد با نام WriteLine() که روی کلاسی به نام Console تعریف شده، در خروجی چاپ می‌کند. پس، اجرای این برنامه منجر به نمایش مقدار 360 می‌شود.

برنامه‌ی بالا با همه‌ی سادگی و اختصار، اجزایی دارد که در اینجا یک معرفی کوتاه از آنها ارائه می‌دهیم:

  • نوع int: در فصل اول گفتیم که نوع‌های درونی .NET در استانداردی به نام CTS تعریف شده‌اند و زبان‌های .NET از نام‌های مستعاری برای ارجاع به این نوع‌ها استفاده می‌کنند. در C# کلمه کلیدی int نام مستعاری است که برای ارجاع به نوع System.Int32 تعریف شده است. می‌توان به جای int از نام اصلی این نوع هم استفاده کرد که البته خیلی کار عاقلانه‌ای نیست.
  • متغیر x: تعریف متغیرها در C# باید با اعلان نوع آنها همراه باشد. البته می‌توان به جای اعلان صریح نوع متغیر از کلمه کلیدی var هم استفاده کرد که در این مورد بعداً توضیحات لازم داده خواهد شد. در مثال بالا، متغیر x دارای نوع int و در نتیجه یک عدد صحیح 32 بیتی است.
  • عملگر *: نماد * نشان‌دهده‌ی عملگر ضرب است که یکی از عملگرهای حسابی (arithmetic operators) در C# است. عملگرهای حسابی و سایر انواع عملگرها را در آینده معرفی می‌کنیم.
  • کلاس Console: در فصل اول گفتیم که کلاس (class) یکی از نوع‌های پنجگانه‌ی C# است که واحد اصلی برنامه‌نویسی شی‌گرا هم محسوب می‌شود. یک کلاس، اعضایی مانند متدها و پراپرتی‌های مرتبط با هم را گروه می‌کند. کلاس Console شامل اعضایی است که به اعمال I/O مربوط هستند.
  • متد WriteLine(): یکی از اعضای کلاس Console که در ساده‌ترین حالت، مقداری را که به عنوان آرگومان دریافت کرده، در خروجی چاپ می‌کند.

باز هم از فصل اول یادآوری می‌کنم که نوع‌هایی مانند کلاس‌ها و اینترفیس‌ها در فضاهای نام (namespaces) تعریف شده‌اند و هر نوعی به یک فضای نام تعلق دارد. کلاس Console هم متعلق به فضای نام System است که به خاطر فعال بودن ویژگی Implicit using directives به طور ضمنی به پروژه اضافه شده و به همین دلیل است که ما می‌توانیم بدون اینکه از System نام ببریم، از کلاس Console استفاده کنیم. اگر ویژگی مذکور را غیرفعال کنیم و یا از نسخه‌های قبل از C# 10 استفاده کنیم، باید یا یک گزاره‌ی using System; به بالای فایل اضافه کنیم و یا اینکه کلاس Console را به شکل System.Console مورد دسترسی قرار دهیم.

بهبود برنامه با تعریف یک متد

یک متد (method) مجموعه‌ای از گزاره‌هاست که نامی به آن اختصاص داده شده تا هر وقت لازم باشد، اجرا شود. فرض کنید می‌خواهیم برنامه‌ای داشته باشیم که یک عدد را بر حسب فوت دریافت کرده و معادل آن را به اینچ برگرداند (هر فوت برابر با 12 اینچ است).

Copy Icon Program.cs
Console.WriteLine(FeetToInches(30));
Console.WriteLine(FeetToInches(100));
            
int FeetToInches(int feet)
{
  int inches = feet * 12;
  return inches;
}

بهترین کار این است که متدی تعریف کنیم که کار تبدیل فوت به اینچ را برای هر عدد ورودی انجام می‌دهد. در کد بالا یک متد با نام FeetToInches() تعریف شده که یک پارامتر از نوع int دارد که feet نامیده شده است. کله کلیدی int در ابتدای تعریف متد بالا به این معناست که نوع بازگشتی متد باید از نوع int باشد. در بدنه‌ی متد هم مقدار ورودی متد در 12 ضرب شده و حاصل این ضرب با استفاده از کلمه کلیدی return برگردانده شده است. به این ترتیب، ما متدی داریم که کار تبدیل فوت به اینچ را برایمان انجام می‌دهد. از این متد دو بار برای مقادیر 30 و 100 فوت استفاده کرده‌ایم.