مقدمه

در برنامه‌نویسی، اغلب با مقادیری سروکار داریم که پس از مقداردهی اولیه، نباید هرگز تغییر کنند. ایجاد داده‌های تغییرناپذیر (Immutable) یک اصل مهم در طراحی نرم‌افزار است که به افزایش پیش‌بینی‌پذیری، امنیت و سادگی کد، به خصوص در برنامه‌های چندنخی (multi-threaded) کمک می‌کند. زبان C# دو کلمه‌ی کلیدی اصلی برای این منظور فراهم می‌کند: const و readonly. هرچند هر دو برای ایجاد مقادیر ثابت به کار می‌روند، اما تفاوت‌های بسیار مهمی در زمان مقداردهی و نحوه‌ی عملکردشان دارند که درک آن‌ها برای هر برنامه‌نویس C# ضروری است.

ثابت‌ها با کلمه‌ی کلیدی const

یک ثابت const، فیلدی است که مقدار آن در زمان کامپایل (compile-time) مشخص و ثابت می‌شود. این یعنی کامپایلر مقدار آن را مستقیماً در کد جایگزین می‌کند.

ویژگی‌های ثابت‌های const

  • مقداردهی در زمان تعریف: یک فیلد const حتماً باید در همان خطی که تعریف می‌شود، مقداردهی شود.
  • مقدار ثابت در زمان کامپایل: مقداری که به یک const اختصاص داده می‌شود، باید برای کامپایلر مشخص باشد. این مقدار می‌تواند یک لیترال (مانند عدد یا رشته) یا یک عبارت متشکل از ثابت‌های دیگر باشد. شما نمی‌توانید نتیجه‌ی یک متد یا یک شیء جدید را به آن اختصاص دهید.
  • ذاتاً استاتیک: فیلدهای const به طور ضمنی static هستند. آن‌ها به کلاس تعلق دارند، نه به یک نمونه‌ی خاص. بنابراین، برای دسترسی به آن‌ها از نام کلاس استفاده می‌کنیم.
Copy Icon Program.cs
public class MathConstants
{
    // A mathematical constant, known at compile-time.
    public const double PI = 3.14159;

    // An application-specific constant.
    public const string AppVersion = "2.1.0";
}

// --- Usage ---
// Accessing constants via the class name.
Console.WriteLine($"Value of PI: {MathConstants.PI}");

// The following line would cause a compile-time error.
// MathConstants.PI = 3.14; // Error: Cannot assign to a constant.

در مثال بالا، PI و AppVersion مقادیر ثابتی هستند که در زمان کامپایل مشخص‌اند و هرگز تغییر نخواهند کرد.

فیلدهای فقط-خواندنی با کلمه‌ی کلیدی readonly

یک فیلد فقط-خواندنی readonly، فیلدی است که مقدار آن در زمان اجرا (run-time) و در لحظه‌ی ایجاد شیء، ثابت می‌شود. این تفاوت اصلی آن با const است.

ویژگی‌های فیلدهای readonly

  • مقداردهی در زمان تعریف یا در سازنده: یک فیلد readonly می‌تواند یا در زمان تعریف مقداردهی شود، یا مقدار آن در سازنده (constructor) کلاس تعیین گردد. پس از خروج از سازنده، مقدار آن دیگر قابل تغییر نیست.
  • مقدار متغیر در زمان اجرا: از آنجایی که مقداردهی در زمان اجرا انجام می‌شود، می‌توان هر مقدار معتبری را به آن اختصاص داد، از جمله نتیجه‌ی فراخوانی یک متد یا یک شیء جدید.
  • می‌تواند نمونه یا استاتیک باشد: برخلاف const، یک فیلد readonly می‌تواند هم به عنوان عضو نمونه (که برای هر شیء مقدار متفاوتی دارد) و هم به عنوان عضو استاتیک (static readonly) تعریف شود.
Copy Icon Program.cs
public class User
{
    // This readonly field is unique to each User instance.
    public readonly Guid UserId;
    public string Username;

    // The readonly field is initialized in the constructor.
    public User(string username)
    {
        // This is a valid place to assign to a readonly field.
        UserId = Guid.NewGuid(); 
        Username = username;
    }

    public void ChangeId()
    {
        // This would cause a compile-time error.
        // UserId = Guid.NewGuid(); // Error: A readonly field cannot be assigned to (except in a constructor).
    }
}

در این مثال، UserId برای هر کاربر یک شناسه‌ی یکتاست که در زمان ساخت کاربر ایجاد و تنظیم می‌شود. پس از آن، این شناسه برای تمام طول عمر شیء User ثابت باقی می‌ماند. هر نمونه‌ی User مقدار UserId متفاوتی خواهد داشت.

مقایسه‌ی دقیق const و readonly

برای درک کامل، بهتر است این دو کلمه‌ی کلیدی را مستقیماً با هم مقایسه کنیم.

ویژگی `const` `readonly`
زمان مقداردهی زمان کامپایل زمان اجرا (در سازنده یا زمان تعریف)
مقدار مجاز باید ثابت زمان کامپایل باشد (لیترال‌ها) می‌تواند هر عبارت معتبری باشد (نتیجه متد و ...)
ماهیت همیشه استاتیک (static) می‌تواند استاتیک یا نمونه (instance) باشد
انواع داده مجاز فقط انواع داده‌ی پایه‌ای و string هر نوع داده‌ای (کلاس، ساختار و ...)

چه زمانی از کدامیک استفاده کنیم؟

  • از const استفاده کنید برای ثابت‌های واقعی و گلوبال که مقدار آن‌ها هرگز در هیچ شرایطی تغییر نمی‌کند. مانند ثابت‌های ریاضی (Math.PI) یا مقادیر رشته‌ای که در کل برنامه ثابت هستند.
  • از readonly استفاده کنید برای مقادیری که به یک نمونه‌ی خاص از کلاس وابسته‌اند و پس از ایجاد آن شیء نباید تغییر کنند. مانند شناسه‌ی یکتا یا تاریخ ایجاد یک رکورد.
  • از static readonly استفاده کنید برای مقادیر ثابتی که در زمان کامپایل مشخص نیستند و باید در زمان اجرا محاسبه یا بارگذاری شوند، اما پس از آن در کل برنامه ثابت باقی می‌مانند. مانند یک شیء تنظیمات که از یک فایل خوانده می‌شود.
Copy Icon Program.cs
public class Configuration
{
    // A shared, immutable configuration object, initialized at runtime.
    public static readonly int Timeout;

    static Configuration()
    {
        // Imagine reading this value from a config file.
        Timeout = 30; // Value is determined at runtime.
    }
}

در این مثال، مقدار Timeout ممکن است از یک فایل خارجی خوانده شود، بنابراین نمی‌تواند const باشد. اما چون پس از بارگذاری نباید تغییر کند، static readonly بهترین انتخاب است.