Avatar ·

Static readonly vs const в C#: когда что использовать?

Всем привет! Я начинающий C# разработчик, и меня мучает вопрос: в чём разница между `static readonly` и `const`? Я понимаю, что оба используются для объявления неизменяемых значений, но когда что применять — ума не приложу. Вот пример: я пишу класс для работы с математическими формулами. Мне нужно объявить число Пи и количество знаков после запятой для вывода результатов. Я сделал так: ```csharp public class MathHelper { public const double Pi = 3.14159; public static readonly int Precision = 3; } ``` Но потом другой разработчик сказал, что `const` для `Pi` — это нормально, а `static readonly` для `Precision` — странно. Почему? Ведь оба значения не меняются. И ещё я заметил, что если я использую `const` в другом проекте, то после изменения значения в этой сборке, другая не видит обновления, пока не перекомпилируется. А с `static readonly` такого нет. Подскажите, пожалуйста, в чём принципиальная разница? Когда нужно использовать `const`, а когда `static readonly`? И можно ли вообще обойтись только `const`? <<>>

Главное отличие: время компиляции против времени выполнения

const и static readonly — это два разных механизма для создания неизменяемых полей, и их выбор зависит от того, когда значение становится известным.

1. const — константа времени компиляции

Значение const должно быть известно на этапе компиляции. Оно подставляется прямо в код в месте использования. Поэтому:

  • Может быть только примитивным типом (int, double, string, bool, enum) или null.
  • Не может быть результатом вызова метода или new (кроме string).
  • Является неявно static — обращаться можно только через имя класса.
  • Важно: Если вы используете const из другой сборки, её значение копируется в вашу сборку при компиляции. Если автор изменит значение, ваша сборка не увидит это до перекомпиляции.

Пример, где const уместен:

public class MathHelper
{
    public const double Pi = 3.141592653589793; // Истинная константа
    public const int DaysInWeek = 7;            // Никогда не изменится
    public const string AppName = "Calculator"; // Строковая константа
}

2. static readonly — константа времени выполнения

Значение static readonly вычисляется один раз при инициализации класса (в статическом конструкторе или при объявлении). Оно не встраивается в код, а хранится как обычное статическое поле. Поэтому:

  • Может быть любого типа, включая ссылочные (классы, массивы).
  • Может быть результатом вызова метода или вычисления.
  • Если используется из другой сборки, изменения подхватятся автоматически (без перекомпиляции клиента).

Пример, где static readonly необходим:

public class Config
{
    public static readonly string ConnectionString = GetConnectionStringFromConfig();
    public static readonly DateTime ReleaseDate = new DateTime(2024, 1, 1);
    public static readonly int[] PrimeNumbers = new[] { 2, 3, 5, 7, 11 }; // Ссылочный тип
    
    private static string GetConnectionStringFromConfig()
    {
        return ConfigurationManager.AppSettings["DbConnection"];
    }
}

Когда что использовать?

  • const — для абсолютно неизменных значений, которые будут одинаковыми во всех версиях: математические константы (Pi, e), единицы измерения (количество минут в часе), строки для атрибутов (например, для [Display(Name = "Имя")]).
  • static readonly — для значений, которые могут измениться в будущем (например, строка подключения к БД, путь к файлу), или которые требуют вычислений (результат метода, new DateTime).

Почему ваш пример спорный?

Precision — это настройка, которая может меняться (например, в новой версии программы захотят выводить 5 знаков). Если объявить её как const, при изменении придётся перекомпилировать все зависимые сборки. Поэтому static readonly здесь правильнее. А Pi — истинная константа, её никогда не изменят, так что const — отличный выбор.

Совет: если сомневаетесь — используйте static readonly. Он безопаснее и гибче. const оставляйте для случаев, когда вы на 100% уверены, что значение не изменится никогда.

<<>>

Дополнение: производительность и межсборные ссылки

Есть ещё один важный нюанс — производительность. const работает быстрее, потому что значение встраивается прямо в IL-код. Но эта микрооптимизация редко оправдывает потерю гибкости.

Также стоит упомянуть, что static readonly можно изменять в статическом конструкторе, что позволяет реализовать ленивую инициализацию (например, читать из файла при первом обращении). const такой возможности не даёт.

И ещё один кейс: если вы пишете библиотеку (DLL) и хотите, чтобы пользователи могли обновлять значения без перекомпиляции своих проектов — используйте static readonly. Если же значение является частью контракта (например, версия API) — лучше const, чтобы оно было гарантированно одинаковым у всех.

В итоге: const — для "вечных" констант, static readonly — для настроек и вычисляемых значений. Не пытайтесь везде использовать только один из них — выбирайте по ситуации.