Static readonly vs const в C#: когда что использовать?
Главное отличие: время компиляции против времени выполнения
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 — для настроек и вычисляемых значений. Не пытайтесь везде использовать только один из них — выбирайте по ситуации.