مقدمه

در درس قبل با کلاس‌ها و اشیاء آشنا شدیم و دیدیم که چگونه یک کلاس به عنوان نقشه‌ای برای ساختن اشیاء عمل می‌کند. فیلدها و متدهایی که تا کنون تعریف کرده‌ایم، همگی اعضای نمونه (Instance Members) بوده‌اند. این یعنی هر شیء (نمونه) که از کلاس ساخته می‌شود، یک کپی مجزا از این اعضا برای خود دارد. اما در C# دسته‌ی دیگری از اعضا به نام اعضای استاتیک (Static Members) وجود دارند. اعضای استاتیک به یک نمونه‌ی خاص از کلاس تعلق ندارند، بلکه متعلق به خود کلاس هستند. این ویژگی به ما امکان می‌دهد تا داده‌ها و رفتارهایی را مدل‌سازی کنیم که بین تمام اشیاء یک کلاس مشترک هستند یا حتی برای استفاده از آن‌ها نیازی به ساختن شیء نداریم.

اعضای استاتیک در مقابل اعضای نمونه

درک تفاوت بین اعضای استاتیک و اعضای نمونه برای استفاده‌ی صحیح از آن‌ها حیاتی است.

  • عضو نمونه (Instance Member): به یک شیء خاص تعلق دارد. برای دسترسی به آن، ابتدا باید با استفاده از new یک شیء بسازید. هر شیء کپی مستقل خود را از فیلدهای نمونه دارد. برای مثال، فیلد Model در کلاس Car یک عضو نمونه است و هر شیء Car مدل مخصوص به خود را دارد.
  • عضو استاتیک (Static Member): به خود کلاس تعلق دارد. برای دسترسی به آن نیازی به ساختن شیء نیست و مستقیماً از طریق نام کلاس فراخوانی می‌شود. تنها یک کپی از یک عضو استاتیک در حافظه وجود دارد که بین تمام اشیاء آن کلاس به اشتراک گذاشته می‌شود. مثال کلاسیک آن، کلاس Math است؛ ما برای دسترسی به ثابت PI، نمی‌نویسیم new Math().PI بلکه مستقیماً می‌نویسیم Math.PI.

برای تعریف یک عضو به عنوان استاتیک، از کلمه‌ی کلیدی static قبل از تعریف آن استفاده می‌کنیم.

فیلدها و متدهای استاتیک

فیلدها، متدها، پراپرتی‌ها و حتی سازنده‌ها می‌توانند به صورت استاتیک تعریف شوند. در این بخش به بررسی فیلدها و متدهای استاتیک می‌پردازیم.

فیلدهای استاتیک

یک فیلد استاتیک برای نگهداری داده‌ای استفاده می‌شود که بین تمام نمونه‌های یک کلاس مشترک است. این فیلدها برای مواردی مانند شمارنده‌ها، ثابت‌ها یا تنظیمات مشترک بسیار مفید هستند. بیایید کلاس Car از درس قبل را با افزودن یک شمارنده‌ی استاتیک برای پیگیری تعداد کل اتومبیل‌های ساخته شده، بهبود دهیم.

Copy Icon Program.cs
public class Car
{
    // Instance field
    public string Model;

    // Static field to count the number of cars created
    public static int NumberOfCars = 0;

    public Car(string model)
    {
        this.Model = model;
        // Increment the static counter each time a new car is created
        Car.NumberOfCars++;
    }
}

// --- Usage ---
Console.WriteLine($"Initial number of cars: {Car.NumberOfCars}"); // Access via class name

Car car1 = new Car("Honda Civic");
Car car2 = new Car("Toyota Camry");

Console.WriteLine($"Total cars created: {Car.NumberOfCars}");

خروجی این کد به شکل زیر خواهد بود:

Initial number of cars: 0
Total cars created: 2
                    

همانطور که می‌بینید، فیلد NumberOfCars به خود کلاس Car تعلق دارد. با هر بار فراخوانی سازنده (یعنی با هر بار ساختن یک شیء جدید)، این شمارنده‌ی مشترک یک واحد افزایش می‌یابد. ما برای دسترسی به آن، مستقیماً از نام کلاس (Car.NumberOfCars) استفاده می‌کنیم.

متدهای استاتیک

یک متد استاتیک نیز مستقیماً روی کلاس فراخوانی می‌شود و نیازی به نمونه‌سازی ندارد. این متدها اغلب برای ایجاد توابع کمکی (Utility Functions) که به وضعیت یک شیء خاص وابسته نیستند، استفاده می‌شوند.

محدودیت کلیدی: متدهای استاتیک نمی‌توانند به اعضای نمونه (فیلدها یا متدهای غیر استاتیک) دسترسی داشته باشند، زیرا آن‌ها به یک شیء خاص مرتبط نیستند و در نتیجه، به کلمه‌ی کلیدی this دسترسی ندارند. آن‌ها فقط می‌توانند به دیگر اعضای استاتیک کلاس دسترسی داشته باشند.

Copy Icon Program.cs
public class Calculator
{
    // An instance method - depends on an object's state (not used here)
    public double LastResult; 

    // A static utility method - does not depend on any object's state
    public static double Add(double a, double b)
    {
        // Cannot access 'LastResult' here because it's an instance member.
        return a + b;
    }
}

// --- Usage ---
// We don't need to create a Calculator object to use Add.
double sum = Calculator.Add(10.5, 5.2);
Console.WriteLine($"Sum: {sum}");

متد Add در کلاس Calculator یک مثال عالی از یک متد استاتیک است. این متد صرفاً یک عملیات روی ورودی‌های خود انجام می‌دهد و به هیچ داده‌ی داخلی یا وضعیت خاصی از یک شیء Calculator نیاز ندارد.

سازنده‌ی استاتیک

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

ویژگی‌های سازنده‌ی استاتیک:

  • با کلمه‌ی کلیدی static مشخص می‌شود و هیچ سطح دسترسی و پارامتری ندارد.
  • در هر کلاس حداکثر یک سازنده‌ی استاتیک می‌تواند وجود داشته باشد.
  • شما نمی‌توانید آن را مستقیماً فراخوانی کنید. CLR (Common Language Runtime) آن را به طور خودکار و فقط یک بار فراخوانی می‌کند؛ این فراخوانی قبل از اولین نمونه‌سازی از کلاس یا قبل از اولین دسترسی به هر یک از اعضای استاتیک آن کلاس انجام می‌شود.
Copy Icon Program.cs
public class DatabaseConnection
{
    public static string ConnectionString;

    // Static constructor to initialize static fields
    static DatabaseConnection()
    {
        Console.WriteLine("Static constructor called!");
        // Imagine loading connection string from a config file here
        ConnectionString = "Server=myServerAddress;Database=myDataBase;";
    }
}

// The static constructor is called automatically before this line
Console.WriteLine(DatabaseConnection.ConnectionString);

سازنده‌های استاتیک برای انجام کارهای راه‌اندازی که فقط یک بار برای کل کلاس باید انجام شوند، مانند خواندن تنظیمات از یک فایل کانفیگ، بسیار مناسب هستند.

کلاس‌های استاتیک

اگر کلاسی دارید که تمام اعضای آن استاتیک هستند، می‌توانید خود کلاس را نیز با کلمه‌ی کلیدی static مشخص کنید. یک کلاس استاتیک یک محفظه برای مجموعه‌ای از متدها و فیلدهایی است که به صورت مستقل از هر شیء عمل می‌کنند.

قوانین کلاس‌های استاتیک:

  • یک کلاس استاتیک فقط می‌تواند شامل اعضای استاتیک باشد.
  • نمی‌توان از یک کلاس استاتیک نمونه‌سازی کرد (استفاده از new برای آن خطای کامپایل می‌دهد).
  • یک کلاس استاتیک نمی‌تواند از کلاس دیگری ارث‌بری کند (همیشه مستقیماً از System.Object ارث‌بری می‌کند).

چه زمانی از اعضا یا کلاس‌های استاتیک استفاده کنیم؟

از اعضا و کلاس‌های استاتیک برای مدل‌سازی مفاهیمی استفاده کنید که به وضعیت یک شیء خاص گره نخورده‌اند.

  • کلاس‌های کمکی (Utility/Helper Classes): بهترین مثال برای کلاس‌های استاتیک هستند. کلاس System.Math یک نمونه‌ی عالی است. شما هرگز نیازی به new Math() ندارید. به همین ترتیب، می‌توانید یک کلاس استاتیک به نام TemperatureConverter با متدهای استاتیک CelsiusToFahrenheit و FahrenheitToCelsius بسازید.
  • داده‌های مشترک: از فیلدهای استاتیک برای نگهداری داده‌هایی استفاده کنید که باید در تمام برنامه‌ی شما یکتا باشند و بین تمام اشیاء یک کلاس به اشتراک گذاشته شوند، مانند شمارنده‌ها یا ثابت‌های سراسری.