مقدمه

تاکنون با انواع داده‌ی پایه‌ای (Primitive Types) مانند int، double، string و bool آشنا شده‌ایم. اما C# به ما این امکان را می‌دهد که انواع داده‌ی سفارشی خود را نیز ایجاد کنیم. دو راهکار قدرتمند برای این کار، استفاده از انواع شمارشی (Enumerations) و ساختارها (Structures) است. این دو، از نوع‌های مقداری (Value Types) هستند و به ما کمک می‌کنند تا کدهایی خواناتر، مدیریت‌پذیرتر و کارآمدتر بنویسیم. در این درس، به تفصیل با نحوه‌ی تعریف و استفاده از enum و struct و تفاوت‌های کلیدی آن‌ها آشنا خواهیم شد.

نوع شمارشی (Enum)

یک نوع شمارشی یا enum، راهی برای تعریف مجموعه‌ای از ثابت‌های نام‌گذاری شده است. این ثابت‌ها معمولاً نمایانگر یک گروه از مقادیر مرتبط و محدود هستند. استفاده از enum به جای اعداد صحیح یا رشته‌های متنی، خوانایی کد را به شدت افزایش داده و احتمال بروز خطا در اثر وارد کردن مقادیر نامعتبر را کاهش می‌دهد.

تعریف یک enum

برای تعریف یک نوع شمارشی از کلمه‌ی کلیدی enum استفاده می‌کنیم. به عنوان مثال، فرض کنید می‌خواهیم روزهای هفته را مدل‌سازی کنیم.

Copy Icon Program.cs
public enum DayOfWeek
{
    Saturday,
    Sunday,
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday
}

در کد بالا، ما یک نوع جدید به نام DayOfWeek تعریف کرده‌ایم. هر یک از اعضای این enum (مانند Saturday، Sunday و ...) یک ثابت نام‌گذاری شده است. به طور پیش‌فرض، نوع داده‌ی پایه برای هر عضو، int است و مقادیر به ترتیب از صفر شروع می‌شوند. یعنی Saturday برابر 0، Sunday برابر 1 و به همین ترتیب تا آخر.

استفاده از enumها

پس از تعریف enum، می‌توانیم متغیری از آن نوع ایجاد کرده و یکی از مقادیر تعریف شده را به آن اختصاص دهیم. این کار باعث می‌شود کد ما بسیار توصیفی و قابل فهم شود.

Copy Icon Program.cs
// Declaring a variable of type DayOfWeek
DayOfWeek today = DayOfWeek.Wednesday;

// Using the enum in a condition
if (today == DayOfWeek.Friday)
{
    Console.WriteLine("It's the weekend!");
}
else
{
    Console.WriteLine("Back to work.");
}

// Enums work great with switch statements
switch (today)
{
    case DayOfWeek.Thursday:
    case DayOfWeek.Friday:
        Console.WriteLine("Almost the weekend!");
        break;
    default:
        Console.WriteLine("It's a weekday.");
        break;
}

همانطور که می‌بینید، استفاده از DayOfWeek.Wednesday بسیار خواناتر از استفاده از یک عدد بی‌معنا مثل 4 است. هر کسی که کد را می‌خواند فوراً متوجه منظور آن می‌شود. ساختار switch نیز با enum ها بسیار خوب کار می‌کند و به شما اجازه می‌دهد منطق‌های شرطی پیچیده را به شکلی تمیز پیاده‌سازی کنید.

تغییر نوع پایه و مقادیر شمارش

شما می‌توانید نوع داده‌ی پایه‌ی یک enum را به هر نوع صحیح دیگری (مانند byte, sbyte, short, ushort, long, ulong) تغییر دهید. این کار زمانی مفید است که بخواهید در مصرف حافظه صرفه‌جویی کنید. همچنین می‌توانید مقادیر عددی دلخواه خود را به اعضای enum اختصاص دهید.

Copy Icon Program.cs
// Enum with a 'byte' underlying type and custom values
public enum UserRole : byte
{
    Guest = 0,
    Member = 10,
    Moderator = 50,
    Admin = 100
}

UserRole currentUserRole = UserRole.Admin;

// To get the integer value, you need to cast it
int roleValue = (int)currentUserRole;
Console.WriteLine($"Role: {currentUserRole}, Value: {roleValue}"); // Output: Role: Admin, Value: 100

در این مثال، نوع پایه‌ی UserRole را به byte تغییر داده‌ایم که فضای کمتری نسبت به int اشغال می‌کند. همچنین مقادیر مشخصی را برای هر نقش کاربری تعیین کرده‌ایم. برای به دست آوردن مقدار عددی یک عضو enum، باید آن را به نوع عددی مورد نظر تبدیل (cast) کنیم.

نوع ساختار (Struct)

ساختار یا struct یکی دیگر از انواع مقداری (Value Type) در C# است که به شما اجازه می‌دهد چندین متغیر مرتبط را در یک واحد داده‌ای کپسوله کنید. ساختارها برای داده‌های کوچک و سبکی که رفتار مقداری دارند، ایده‌آل هستند. بر خلاف کلاس‌ها که نوع ارجاعی (Reference Type) هستند، متغیرهای از نوع struct خود داده را مستقیماً در خود نگه می‌دارند، نه یک ارجاع به داده در حافظه.

تعریف یک struct

برای تعریف یک ساختار، از کلمه‌ی کلیدی struct استفاده می‌کنیم. یک ساختار می‌تواند شامل فیلدها (متغیرها)، پراپرتی‌ها و متدها باشد، دقیقاً مانند یک کلاس.

Copy Icon Program.cs
public struct Point
{
    // Fields
    public double X;
    public double Y;

    // A constructor to initialize the fields
    public Point(double x, double y)
    {
        X = x;
        Y = y;
    }

    // A method
    public void Display()
    {
        Console.WriteLine($"({X}, {Y})");
    }
}

در کد بالا، یک ساختار به نام Point تعریف کرده‌ایم که برای نمایش یک نقطه در فضای دو بعدی به کار می‌رود. این ساختار دو فیلد عمومی X و Y، یک سازنده (constructor) برای مقداردهی اولیه فیلدها، و یک متد برای نمایش مختصات دارد.

ایجاد و استفاده از structها

برای ایجاد یک نمونه از یک ساختار، معمولاً از کلمه‌ی کلیدی new به همراه سازنده‌ی آن استفاده می‌کنیم. پس از ایجاد نمونه، می‌توانیم به اعضای آن دسترسی پیدا کنیم.

Copy Icon Program.cs
// Create an instance of the Point struct using its constructor
Point p1 = new Point(10, 20);

// Access its members
Console.WriteLine("Initial coordinates:");
p1.Display(); // Output: (10, 20)

// Change its field values
p1.X = 50;
Console.WriteLine("New coordinates:");
p1.Display(); // Output: (50, 20)

یک نکته‌ی مهم در مورد ساختارها این است که چون نوع مقداری هستند، وقتی یک متغیر struct را به متغیر دیگری اختصاص می‌دهید، یک کپی کامل از داده ایجاد می‌شود. این رفتار با کلاس‌ها که در آن فقط ارجاع کپی می‌شود، متفاوت است. این موضوع را در درس بعدی به تفصیل بررسی خواهیم کرد.

چه زمانی از struct به جای class استفاده کنیم؟

انتخاب بین struct و class یک تصمیم مهم در طراحی است. به طور کلی، از struct در شرایط زیر استفاده کنید:

  • نمایندگی یک مقدار واحد: زمانی که نوع داده‌ی شما به طور منطقی یک مقدار واحد را نشان می‌دهد، مانند یک عدد مختلط، یک رنگ، یا یک نقطه.
  • اندازه کوچک: زمانی که اندازه نمونه‌های نوع شما کوچک است (معمولاً کمتر از 16 بایت).
  • عدم تغییرپذیری (Immutability): ساختارها برای داده‌هایی که پس از ایجاد تغییر نمی‌کنند، گزینه‌ی بسیار خوبی هستند.
  • عدم نیاز به وراثت: ساختارها از وراثت پشتیبانی نمی‌کنند (نمی‌توانند از کلاس یا ساختار دیگری ارث‌بری کنند).

در سایر موارد، به خصوص برای موجودیت‌های پیچیده‌تر که هویت و طول عمر مشخصی دارند (مانند Customer، Order یا Window)، استفاده از class انتخاب صحیح است.