Инструкции объявления
Оператор объявления объявляет новую переменную и при необходимости инициализирует ее. Все переменные имеют объявленный тип. Дополнительные сведения о типах см. в статье о системе типов .NET. Как правило, объявление включает тип и имя переменной. Он также может включать инициализацию: оператор , =
за которым следует выражение. Тип можно заменить на var
. Объявление или выражение может включать модификатор ref
для объявления, что новая переменная ссылается на существующее расположение хранилища.
Неявно типизированные локальные переменные
Переменные, объявленные в области метода, могут иметь неявный "тип". var
Неявно типизированная локальная переменная строго типизирована так же, как если бы вы сами объявили тип, но компилятор определяет тип. Следующие два объявления a
и b
функционально эквивалентны:
var a = 10; // Implicitly typed.
int b = 10; // Explicitly typed.
Важно!
При использовании var
с включенными ссылочными типами, допускающими значение NULL, всегда подразумевается ссылочный тип, допускающий значение NULL, даже если тип выражения не допускает значения NULL. Анализ состояния NULL в компиляторе обеспечивает защиту от разыменования возможного значения null
. Если переменная никогда не назначается выражению, которое может иметь значение NULL, компилятор не выдаст никаких предупреждений. Если переменная присваивается выражению, которое может иметь значение NULL, необходимо проверить, что оно не равно NULL, перед разыменованием, чтобы избежать предупреждений.
Ключевое слово var
обычно используется с выражениями вызова конструктора. Использование var
позволяет вам не повторять имя типа при объявлении переменной и создании экземпляра объекта, как показано в следующем примере:
var xs = new List<int>();
Начиная с C# 9.0 вы можете использовать выражение new
с целевым типом в качестве альтернативы:
List<int> xs = new();
List<int>? ys = new();
При сопоставлении шаблонов в шаблонеvar
используется ключевое слово var
.
В следующем примере показаны два выражения запросов. В первом выражении использование разрешено var
, но не является обязательным, так как тип результата запроса можно явно указать как IEnumerable<string>
. Однако во втором выражении var
позволяет результату быть коллекцией анонимных типов, а имя этого типа доступно только компилятору. Использование var
делает создание нового класса для результата необязательным. В примере 2 foreach
переменная item
итерации также должна быть неявно типизированной.
// Example #1: var is optional when
// the select clause specifies a string
string[] words = { "apple", "strawberry", "grape", "peach", "banana" };
var wordQuery = from word in words
where word[0] == 'g'
select word;
// Because each element in the sequence is a string,
// not an anonymous type, var is optional here also.
foreach (string s in wordQuery)
{
Console.WriteLine(s);
}
// Example #2: var is required because
// the select clause specifies an anonymous type
var custQuery = from cust in customers
where cust.City == "Phoenix"
select new { cust.Name, cust.Phone };
// var must be used because each item
// in the sequence is an anonymous type
foreach (var item in custQuery)
{
Console.WriteLine("Name={0}, Phone={1}", item.Name, item.Phone);
}
Ссылочные локальные переменные
Ключевое слово добавляется ref
перед типом переменной, объявляемой локальной ref
. Локальный ref
— это переменная, которая ссылается на другое хранилище. Предположим, что GetContactInformation
метод объявлен в качестве возвращаемого значения ссылки:
public ref Person GetContactInformation(string fname, string lname)
Давайте противопоставим эти два назначения:
Person p = contacts.GetContactInformation("Brandie", "Best");
ref Person p2 = ref contacts.GetContactInformation("Brandie", "Best");
Переменная p
содержит копию возвращаемого значения из GetContactInformation
. Это отдельное место хранения от возвращаемого ref
из GetContactInformation
. Если вы изменяете какое-либо свойство p
, вы изменяете копию Person
.
Переменная p2
ссылается на расположение хранилища для возвращаемого ref
объекта .GetContactInformation
Это то же хранилище, что и возвращаемый ref
из GetContactInformation
. Если вы изменяете какое-либо свойство p2
, вы изменяете этот отдельный Person
экземпляр .
Вы можете таким же образом обратиться к значению по ссылке. В некоторых случаях обращение к значению по ссылке повышает производительность, поскольку позволяет избежать потенциально затратной операции копирования. Например, в следующей инструкции показано, как можно определить локальное ссылочное значение, используемое для ссылки на значение.
ref VeryLargeStruct reflocal = ref veryLargeStruct;
Ключевое слово ref
используется перед объявлением локальной переменной и перед значением во втором примере. Если не включить оба ref
ключевых слова в объявление переменной и присваивание в обоих примерах, возникает ошибка компилятора CS8172: "Не удается инициализировать переменную по ссылке со значением".
ref VeryLargeStruct reflocal = ref veryLargeStruct; // initialization
refLocal = ref anotherVeryLargeStruct; // reassigned, refLocal refers to different storage.
Локальные переменные Ref по-прежнему должны быть инициализированы при их объявлении.
В следующем примере определяется класс NumberStore
, в котором хранится массив целочисленных значений. Метод FindNumber
возвращает по ссылке первое число, которое не меньше переданного в аргументе значения. Если такое число не найдено, метод возвращает число с индексом 0.
using System;
class NumberStore
{
int[] numbers = { 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023 };
public ref int FindNumber(int target)
{
for (int ctr = 0; ctr < numbers.Length; ctr++)
{
if (numbers[ctr] >= target)
return ref numbers[ctr];
}
return ref numbers[0];
}
public override string ToString() => string.Join(" ", numbers);
}
В следующем примере вызывается метод NumberStore.FindNumber
, который извлекает первое значение не меньше 16. После этого вызывающий объект удваивает значение, возвращаемое методом. Как видно из выходных данных этого примера, изменение отражается в значении элементов массива в экземпляре NumberStore
.
var store = new NumberStore();
Console.WriteLine($"Original sequence: {store.ToString()}");
int number = 16;
ref var value = ref store.FindNumber(number);
value *= 2;
Console.WriteLine($"New sequence: {store.ToString()}");
// The example displays the following output:
// Original sequence: 1 3 7 15 31 63 127 255 511 1023
// New sequence: 1 3 7 15 62 63 127 255 511 1023
Без использования возвращаемых ссылочных значений такая операция выполняется путем возврата индекса элемента массива вместе с его значением. После этого вызывающий объект использует индекс для изменения значения в отдельном вызове метода. Тем не менее вызывающий объект также может изменить индекс для доступа к другим значениям массива и их изменения.
В следующем примере показано, как FindNumber
можно переписать метод для использования локального переназначения ссылки:
using System;
class NumberStore
{
int[] numbers = { 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023 };
public ref int FindNumber(int target)
{
ref int returnVal = ref numbers[0];
var ctr = numbers.Length - 1;
while ((ctr >= 0) && (numbers[ctr] >= target))
{
returnVal = ref numbers[ctr];
ctr--;
}
return ref returnVal;
}
public override string ToString() => string.Join(" ", numbers);
}
Эта вторая версия работает эффективнее с более длинными последовательностями в сценариях, в которых искомое число находится ближе к концу массива, так как итерация массива выполняется от конца к началу, что приводит к проверке меньшего числа элементов.
Компилятор применяет правила области к ref
переменным: ref
локальным переменным, ref
параметрам и ref
полям в ref struct
типах. Правила гарантируют, что ссылка не будет выходить за пределы объекта, на который она ссылается. См. раздел о правилах определения области статьи о параметрах метода.
ref и readonly
Модификатор readonly
может применяться к локальным ref
переменным и ref
полям. Модификатор readonly
влияет на выражение справа от нее. См. следующие примеры объявлений:
ref readonly int aConstant; // aConstant can't be value-reassigned.
readonly ref int Storage; // Storage can't be ref-reassigned.
readonly ref readonly int CantChange; // CantChange can't be value-reassigned or ref-reassigned.
- Переназначение значения означает, что значение переменной переназначается.
- Присвоение ссылок означает, что переменная теперь ссылается на другой объект.
Объявления readonly ref
и readonly ref readonly
допустимы только для ref
полей в ref struct
.
ссылка с областью действия
Контекстное ключевое слово scoped
ограничивает время существования значения. Модификатор scoped
ограничивает время существования ref-safe-to-escape или safe-to-escape соответственно текущим методом. Фактически добавление модификатора scoped
подтверждает, что код не продлевает время существования переменной.
Можно применить к scoped
параметру или локальной переменной. Модификатор scoped
может применяться к параметрам и локальным переменным, если типом ref struct
является . В противном случае модификатор scoped
может применяться только к локальным переменным, которые являются ссылочных типов. Сюда входят локальные переменные, объявленные модификатором ref
, и параметры, объявленные модификаторами in
, ref
или out
.
Модификатор scoped
неявно добавляется this
в в методах, объявленных struct
в параметрах , out
и ref
, если типом является ref struct
.