Menu

Переменные в C#: var, const, readonly, область видимости и имена

Как объявлять и присваивать переменные в C#, когда var уместен, а когда нет, как работает область видимости, чем отличаются const, readonly и static readonly, соглашения об именах и ошибка неприсвоенной переменной.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Переменная это именованное место хранения с фиксированным типом. В C# её объявляют с типом и именем и при желании сразу задают значение в том же операторе. Тип потом никогда не меняется: переменная int хранит целые числа всё время своего существования.

Вывод:

Priya bought 5 items for 20.25
10

Присваивание значения неправильного типа это ошибка компиляции: quantity = "five"; завершается ошибкой CS0029 (cannot implicitly convert type 'string' to 'int'). Преобразование между типами выполняется явно; см. преобразование типов.

var: пусть компилятор выведет тип

var объявляет локальную переменную, тип которой выводится из инициализатора. Это не динамическая типизация: тип фиксируется при компиляции точно так же, как если бы вы написали его сами.

Вывод:

String
Int32
Decimal
Double
List`1

У var три правила. Он работает только для локальных переменных (не для полей и параметров). Переменная должна быть инициализирована в объявлении: var x; это ошибка CS0818, «Implicitly-typed variables must be initialized». И у инициализатора должен быть тип: var nothing = null; не компилируется, потому что у одиночного null нет типа.

Когда его использовать, решает стиль команды. Обычная рекомендация: использовать var, когда тип очевиден из правой части, и писать тип, когда нет:

var orders = new List<Order>();         // obvious: var is fine
var reader = new StreamReader(path);    // obvious
decimal discount = CalculateDiscount(); // not obvious: name the type
int count = items.Count();              // makes the intent clear

Тонкая ловушка: var total = 0; делает total переменной типа int, поэтому прибавить к ней потом 2.5 не получится, а var price = 10; это int, а не decimal. Если тип литерала не тот, что вам нужен, напишите тип или добавьте суффикс (10m, 0.0).

Область видимости

Локальная переменная существует от объявления до конца блока { }, в котором она объявлена. Переменные, объявленные в заголовке цикла или в теле if, после них не видны.

Вывод:

high
240

В отличие от C и C++, C# не позволяет во внутреннем блоке объявить локальную переменную с тем же именем, что и во внешнем (ошибка CS0136): имя должно означать одно и то же во всём методе. Два соседних блока могут использовать одно имя повторно, как это делают два отдельных цикла for, каждый из которых объявляет i.

Определённое присваивание

У локальных переменных нет значения по умолчанию. Компилятор отслеживает каждый путь выполнения метода и отказывается компилировать код, если переменная может быть прочитана до присваивания:

int discount;
if (isMember)
{
    discount = 10;
}
Console.WriteLine(discount);
// error CS0165: Use of unassigned local variable 'discount'

Исправьте это инициализацией при объявлении (int discount = 0;) или присваиванием в каждой ветке (добавьте else). Поля и элементы массивов ведут себя иначе: им автоматически присваивается значение по умолчанию для их типа (0, false, null).

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

const это значение, зафиксированное при компиляции. Его нужно инициализировать константным выражением (литералом, другой константой или арифметикой над ними), и это может быть только число, char, bool, string, перечисление или null.

Вывод:

Price in EUR
120.00 EUR
350

Константное поле неявно static, поэтому к нему обращаются через имя типа (Pricing.VatRate), а запись static const это ошибка. Компилятор копирует значение в каждое место использования, поэтому константы ничего не стоят во время выполнения, и поэтому же открытая константа в библиотеке должна быть значением, которое никогда не изменится (например, число дней в неделе).

readonly и static readonly

Поле readonly можно присвоить только в его объявлении или в конструкторе его класса. После создания объекта оно не может измениться. В отличие от const, его значение вычисляется во время выполнения, может быть любого типа и может различаться у разных объектов.

Вывод:

ACC-17 opened 2024-05-02
Launch: 2020-01-15
basic, pro

Последние строки показывают частое заблуждение: readonly защищает поле, а не объект, на который оно ссылается. Plans всегда будет ссылаться на один и тот же массив, но элементы массива по-прежнему можно менять. Для действительно неизменяемых коллекций используйте ReadOnlyCollection<T> или ImmutableArray<T>.

Сводка по всем трём:

constreadonlystatic readonly
Значение известноПри компиляцииВо время выполненияВо время выполнения
Допустимые типыЧисла, char, bool, string, перечисления, nullЛюбыеЛюбые
Где задаётсяТолько в объявленииВ объявлении или конструктореВ объявлении или статическом конструкторе
У каждого объекта или общееОбщее (неявно static)У каждого объектаОбщее
Локальные переменныеДа (const int x = 1;)НетНет

Соглашения об именах

Имена в C# чувствительны к регистру (total и Total это разные переменные) и должны начинаться с буквы или подчёркивания. Соглашения Microsoft, которым следует почти весь код на C#:

  • Локальные переменные и параметры: camelCase (orderCount, unitPrice).
  • Константы, свойства, методы, классы и открытые поля: PascalCase (MaxItems, VatRate).
  • Закрытые поля: _camelCase с подчёркиванием в начале (_balance), широко распространённое соглашение в коде .NET.
  • Логические переменные читаются как вопросы: isActive, hasDiscount, canRetry.
  • Избегайте сокращений и венгерских префиксов (strName, iCount).

Ключевое слово C# нельзя использовать как имя, если не поставить перед ним @: var @class = "5B"; допустимо, что иногда полезно для сгенерированного кода или имён полей JSON.

Обмен значениями и множественное присваивание

Начиная с C# 7, кортежи позволяют присвоить несколько переменных в одном операторе, что даёт обмен значений в одну строку:

Вывод:

20 10
Lisbon: 545,000

Вторая форма это деконструкция: она объявляет city как string и population как int из кортежа. Подробнее в разделе кортежи.

Часто задаваемые вопросы

Что такое var в C#?

var говорит компилятору вывести тип локальной переменной из значения, которым она инициализирована: var count = 5; объявляет int. Переменная по-прежнему статически типизирована и не сможет потом хранить строку. var работает только для локальных переменных, которые инициализируются в том же операторе.

Чем const отличается от readonly в C#?

const это константа времени компиляции: её значение должно быть известно при компиляции (числа, строки, bool, null), она неявно статическая, а значение копируется в каждое место использования. Поле readonly задаётся один раз во время выполнения, в объявлении или в конструкторе, может иметь любой тип и может различаться у разных объектов.

Когда использовать static readonly вместо const?

Используйте static readonly для общего значения, которое не может быть константой времени компиляции, например DateTime, массива или объекта, или для открытого значения, которое может меняться между версиями библиотеки. Поскольку значения const копируются в вызывающие сборки, изменение открытой константы требует перекомпиляции всех сборок, которые её используют.

Что означает «use of unassigned local variable» в C#?

Ошибка CS0165: компилятор нашёл путь, на котором локальная переменная читается до того, как ей было присвоено значение. У локальных переменных в C# нет значения по умолчанию. Инициализируйте переменную при объявлении или убедитесь, что каждая ветка if/switch присваивает её до использования.

Что использовать в C#, var или явные типы?

Подходит и то и другое; команды выбирают свой стиль. Распространённое правило: использовать var, когда тип очевиден из правой части (var list = new List<string>();), и писать тип, когда он не очевиден (decimal total = GetTotal();). Кодовая база среды выполнения .NET примерно следует этому правилу.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ