Инструкции объявления

Оператор объявления объявляет новую переменную и при необходимости инициализирует ее. Все переменные имеют объявленный тип. Дополнительные сведения о типах см. в статье о системе типов .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.

См. также