Const vs Readonly в C#: в чём разница и когда что применять?
Каждый разработчик на C# рано или поздно сталкивается с дилеммой: объявить значение как const или как readonly? На первый взгляд, оба ключевых слова делают одно и то же — запрещают изменение переменной после инициализации. Но копни глубже — и откроется целая пропасть различий, влияющих на архитектуру, производительность и даже безопасность кода.
В этой статье мы не просто перечислим сухие факты из документации. Мы разберём реальные сценарии, покажем подводные камни и поможем вам сделать осознанный выбор. Поехали!
Что такое const в C#?
Const — это константа времени компиляции. Её значение должно быть известно на момент компиляции и не может меняться в рантайме.
- Может быть только примитивных типов:
int,double,string,bool,enumиnull. - Неявно
static— принадлежит типу, а не экземпляру. - Значение подставляется прямо в код на этапе компиляции (как макрос в C++).
- Нельзя использовать с
newили ссылочными типами (кромеstring).
public class MathConstants
{
public const double Pi = 3.14159;
public const int MaxRetries = 5;
public const string AppName = "MyApp";
// Ошибка! Ссылочные типы (кроме string) нельзя
// public const List<int> Numbers = new List<int>();
}
Когда компилятор встречает const, он буквально встраивает значение в IL-код. Это значит, что если вы используете константу из другой сборки, и потом измените её значение, старые сборки всё ещё будут использовать старое значение — пока не будут перекомпилированы.
Что такое readonly в C#?
Readonly — это поле, которое можно инициализировать только в момент объявления или в конструкторе класса. После этого изменить его нельзя.
- Может быть любого типа: как значимого, так и ссылочного.
- Может быть как статическим, так и экземплярным.
- Значение вычисляется в рантайме (например, можно использовать
newили вызов метода). - Не подставляется в код — это обычное поле с защитой от записи.
public class Config
{
public readonly DateTime CreatedAt = DateTime.Now;
public readonly List<string> Tags;
public static readonly Guid InstanceId = Guid.NewGuid();
public Config()
{
Tags = new List<string> { "csharp", "tutorial" };
// Ошибка! Нельзя изменить после конструктора
// Tags = new List<string>();
}
}
Важный нюанс: readonly защищает только ссылку, а не содержимое объекта. Если у вас readonly List<int>, вы не сможете присвоить новый список, но спокойно можете добавлять или удалять элементы.
Сравнение const и readonly: таблица различий
Чтобы закрепить материал, вот наглядное сравнение:
| Критерий | const | readonly |
|---|---|---|
| Время вычисления | Компиляция | Выполнение (рантайм) |
| Типы данных | Только примитивы + string | Любые типы |
| Статичность | Неявно static | Может быть static или instance |
| Изменяемость | Никогда | Только в конструкторе |
| Доступ | Как static поле | Как обычное поле |
| Подстановка | Да (встраивается в код) | Нет (обычное поле) |
| Производительность | Максимальная (нет поля) | Немного медленнее (обращение к полю) |
Когда использовать const?
Используйте const для значений, которые:
- Абсолютно неизменны и никогда не поменяются: математические константы (
Math.PI), коды ошибок, названия конфигураций. - Примитивны и не требуют вычислений в рантайме.
- Локальны для одной сборки, либо вы готовы перекомпилировать все зависимые проекты при изменении.
// Хороший пример для const
public class HttpStatusCodes
{
public const int Ok = 200;
public const int NotFound = 404;
public const int InternalServerError = 500;
}
Когда использовать readonly?
Выбирайте readonly, когда:
- Значение зависит от рантайма: дата создания, GUID, результат вычисления.
- Тип — ссылочный: списки, словари, пользовательские классы.
- Нужны разные значения для разных экземпляров: например, идентификатор заказа.
- Вы пишете библиотеку и не хотите, чтобы изменение константы сломало клиентов без перекомпиляции.
// Хороший пример для readonly
public class Order
{
public readonly Guid OrderId;
public readonly DateTime CreatedAt;
public readonly List<OrderItem> Items;
public Order()
{
OrderId = Guid.NewGuid();
CreatedAt = DateTime.UtcNow;
Items = new List<OrderItem>();
}
}
Подводные камни и частые ошибки
1. Проблема с встраиванием const
Представьте: у вас есть библиотека MyLib.dll с константой public const int Version = 1;. Вы компилируете приложение App.exe, которое использует эту константу. Компилятор встраивает значение 1 в App.exe. Если вы обновите MyLib.dll до версии 2, но не перекомпилируете App.exe, он всё равно будет использовать 1. Баг гарантирован.
Решение: Для публичных API используйте static readonly вместо const.
2. Readonly и изменяемые объекты
Помните: readonly
Блоги
Рекомендаций по книгам
Читайте также
Основные методы работы со строками в Python
Code Review на PHP: 8 главных вещей, которые должен проверять каждый разработчик
Как составить сложный WHERE в Laravel Eloquent: 4 способа с примерами
C++ в 2025 году: новые возможности для начинающих программистов
Работа с датами и временем в JavaScript: от А до Я для Node.js