В чём разница между модификаторами const, readonly и static в C#?

Ответ

Эти модификаторы решают разные задачи, связанные с областью видимости, временем инициализации и принадлежностью данных.

const (константа времени компиляции)

  • Значение должно быть известно на этапе компиляции.
  • Инициализация: Только при объявлении.
  • Неявно static: Обращение через имя класса: ClassName.ConstName.
  • Типы данных: Только встроенные типы (int, double, bool, char, string).
  • Пример:
    public class MathConstants
    {
        public const double Pi = 3.14159;
        public const string AppName = "MyApp";
    }
    // Использование:
    double circumference = 2 * MathConstants.Pi * radius;

readonly (константа времени выполнения)

  • Значение может быть вычислено во время выполнения.
  • Инициализация: При объявлении или в конструкторе класса (для полей экземпляра).
  • Может быть static или нет.
  • Типы данных: Любые.
  • Пример:

    public class AppService
    {
        public readonly DateTime StartTime = DateTime.Now; // Инициализация при объявлении
        private readonly ILogger _logger;
    
        public AppService(ILogger logger)
        {
            _logger = logger; // Инициализация в конструкторе
        }
    }

static (принадлежность типу)

  • Указывает, что член (поле, свойство, метод) принадлежит самому типу (классу), а не его экземплярам.
  • Комбинируется с readonly: static readonly — распространённая практика для констант времени выполнения на уровне класса.
  • Пример:

    public class Configuration
    {
        // Константа времени выполнения, общая для всех экземпляров и потоков.
        public static readonly string ConnectionString = LoadFromConfig();
    
        public static int InstanceCount { get; private set; } // Статическое свойство
    
        public Configuration() { InstanceCount++; }
    }
Сводная таблица: Критерий const readonly (поле экземпляра) static readonly
Время инициализации Компиляция Выполнение (в конструкторе) Выполнение (статический конструктор)
Принадлежность Классу (static) Экземпляру класса Классу (static)
Допустимые типы Примитивы, string Любые Любые
Изменяемость Никогда Только в конструкторе Только в статическом конструкторе

Ответ 18+ 🔞

Давай разберём эту триаду, а то народ постоянно путается, будто это три одинаковые хуйни. А там, блядь, разница — как между "выпить чаю" и "получить по ебалу чайником".

const — это как татуха на жопе, на всю жизнь

Значение должно быть известно прямо на этапе компиляции, вот прямо когда ты код пишешь. Как будто ты объявляешь: "Пи — это 3.14, и точка нахуй". Менять потом — только с кожей срезать, перекомпилировать весь проект.

  • Инициализация: Только тут же, в момент объявления. Позже — ни-ни.
  • Неявно static: Обращаешься к ней через имя класса, типа MathConstants.Pi. Экземпляр для этого дерьма не нужен.
  • Типы: Только примитивы (int, bool) да string. Сложные объекты сюда не впихнёшь.
  • Пример, чтобы было понятно:
    public class Settings
    {
        public const int MaxRetryCount = 3; // Всё, тройка навеки в камне.
        public const string AppName = "MySuperApp"; // И название не поменять.
    }
    // Используется везде просто по имени класса
    for (int i = 0; i < Settings.MaxRetryCount; i++) { ... }

readonly — это как обещание, которое даёшь один раз

Значение можно вычислить уже во время работы программы. Дал слово — и держишь. Менять можно только в одном месте — в конструкторе того класса, где поле объявлено. После этого — всё, пиши пропало, поле заморожено.

  • Инициализация: Или сразу при объявлении, или в любом конструкторе этого класса. Больше нигде.
  • Может быть как обычным полем экземпляра, так и статическим (static readonly).
  • Типы: Любые, хоть целый List<string> тули.
  • Пример из жизни:

    public class OrderProcessor
    {
        // Вычислили при создании объекта и зафиксировали
        public readonly DateTime OrderCreated = DateTime.Now;
        // Получили извне при создании и больше не трогаем
        private readonly IPaymentGateway _gateway;
    
        public OrderProcessor(IPaymentGateway gateway)
        {
            _gateway = gateway; // Вот здесь инициализируем, и всё.
        }
    
        // А вот так НЕЛЬЗЯ, будет ошибка:
        // public void ChangeGateway() { _gateway = newGateway; }
    }

static — это общая табличка на всех, а не личная кружка

Это вообще про другое. Говорит, что член (поле, метод, свойство) принадлежит не какому-то отдельному объекту, а всему классу целиком. Как общий холодильник на этаже. Один на всех.

  • Часто комбинируется с readonly: Получается static readonly — идеальная штука для глобальных настроек, которые вычисляются при старте программы и потом не меняются.
  • Пример, где всё вместе:

    public class GlobalConfig
    {
        // Константа времени КОМПИЛЯЦИИ, общая для всех.
        public const string Version = "1.0";
    
        // Константа времени ВЫПОЛНЕНИЯ, общая для всех. Инициализируется при первом обращении к классу.
        public static readonly string ConnectionString = LoadFromSecretsVault();
    
        // Просто статическое поле, которое МОЖНО менять (обычно не стоит).
        public static int TotalRequestsProcessed = 0;
    }

Краткая шпаргалка, чтобы не еб... не путаться:

Признак const readonly (поле объекта) static readonly
Когда значение фиксируется При компиляции При создании объекта (в конструкторе) При первом обращении к классу
Чьё это Класса (неявно static) Конкретного экземпляра объекта Класса (явно static)
Какие типы можно Примитивы, string Любые, хоть HttpClient Любые
Можно ли изменить Никогда, только пересобрать проект Только в конструкторе этого класса Только в статическом конструкторе

Итог простой:

  • Нужна жёсткая константа, известная при написании кода (число, флаг, название) — const.
  • Нужно зафиксировать значение, которое узнаёшь только при запуске или передаёшь извне в объект — readonly.
  • Нужно, чтобы это значение было одно на все экземпляры класса — добавляешь static к readonly.

Вот и вся магия. Главное — не пытаться const присвоить DateTime.Now или результат вызова метода, а то компилятор тебе такое в уши надует, что мало не покажется.