Поделиться через


9 переменных

9.1 Общие

Переменные представляют собой места хранения, Каждая переменная имеет тип, определяющий, какие значения можно хранить в переменной. C# — это типобезопасный язык, и компилятор C# гарантирует, что значения, хранящиеся в переменных, всегда имеют соответствующий тип. Значение переменной можно изменить с помощью назначения или использования ++ операторов.--

Перед получением значения переменная должна быть определенно назначена (§9.4).

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

Категории переменных 9.2

9.2.1 Общие

C# определяет восемь категорий переменных: статические переменные, переменные экземпляра, элементы массива, параметры значения, входные параметры, ссылочные параметры, выходные параметры и локальные переменные. Подклаузы, которые описаны ниже, описывают каждую из этих категорий.

Пример. В следующем коде

class A
{
    public static int x;
    int y;

    void F(int[] v, int a, ref int b, out int c, in int d)
    {
        int i = 1;
        c = a + b++ + d;
    }
}

xявляется статической переменной, является переменной экземпляра, y v[0] является элементом массива, a является параметром значения, является ссылочным параметромc, b является выходным параметром, d является входным параметром и i является локальной переменной. пример конца

Статические переменные 9.2.2

Поле, объявленное модификатором, static является статической переменной. Статическая переменная возникает до выполнения конструктора static (§15.12) для своего содержащего типа и перестает существовать, когда связанный домен приложения перестает существовать.

Начальное значение статической переменной — это значение по умолчанию (§9.3) типа переменной.

Для проверки определенного назначения статические переменные считаются первоначально назначенными.

Переменные экземпляра 9.2.3

9.2.3.1 Общие

Поле, объявленное без static модификатора, является переменной экземпляра.

Переменные экземпляра 9.2.3.2 в классах

Переменная экземпляра класса возникает при создании нового экземпляра этого класса и перестает существовать, если нет ссылок на этот экземпляр, а метод завершения экземпляра (если есть) выполнен.

Начальное значение переменной экземпляра класса является значением по умолчанию (§9.3) типа переменной.

Для проверки определенного назначения переменная экземпляра класса считается первоначально назначенной.

Переменные экземпляра 9.2.3.3 в структуры

Переменная экземпляра структуры имеет точно то же время существования, что и переменная структуры, к которой она принадлежит. Другими словами, когда переменная типа структуры вступает в существование или перестает существовать, поэтому и переменные экземпляра структуры.

Начальное состояние назначения переменной экземпляра структуры совпадает с struct состоянием содержащей переменной. Иными словами, если переменная структуры считается первоначально назначенной, поэтому также являются ее переменными экземпляра, и когда переменная структуры считается первоначально неназначаемой, ее переменные экземпляра также не назначены.

Элементы массива 9.2.4

Элементы массива возникают при создании экземпляра массива и перестают существовать, если нет ссылок на этот экземпляр массива.

Начальное значение каждого из элементов массива является значением по умолчанию (§9.3) типа элементов массива.

Для проверки определенного назначения элемент массива считается изначально назначенным.

Параметры значения 9.2.5

Параметр значения возникает при вызове члена функции (метода, конструктора экземпляра, доступа или оператора) или анонимной функции, к которой принадлежит параметр, инициализируется со значением аргумента, заданного в вызове. Параметр значения обычно перестает существовать при завершении выполнения тела функции. Однако если параметр значения захватывается анонимной функцией (§12.19.6.2), его время существования продолжается по крайней мере до тех пор, пока делегат или дерево выражений, созданное из этой анонимной функции, может быть разрешено для сборки мусора.

Для проверки определенного назначения параметр значения считается изначально назначенным.

Параметры значений рассматриваются далее в разделе §15.6.2.2.

Параметры ссылок 9.2.6

Ссылочный параметр — это эталонная переменная (§9.7), которая возникает при вызове члена функции, делегата, анонимной функции или локальной функции, а ее референт инициализируется переменной, заданной в качестве аргумента в этом вызове. Ссылочный параметр перестает существовать после завершения выполнения тела функции. В отличие от параметров значения, ссылочный параметр не должен быть записан (§9.7.2.9).

Следующие правила определенного назначения применяются к эталонным параметрам.

Примечание. Правила выходных параметров отличаются и описаны в разделе (§9.2.7). конечная заметка

  • Переменная должна быть определенно назначена (§9.4), прежде чем она может быть передана в качестве ссылочного параметра в члене функции или вызове делегата.
  • В члене функции или анонимной функции эталонный параметр считается изначально назначенным.

Ссылочные параметры рассматриваются далее в разделе §15.6.2.3.3.

Параметры вывода 9.2.7

Выходной параметр является ссылочной переменной (§9.7), которая возникает при вызове члена функции, делегата, анонимной функции или локальной функции, а ее референт инициализируется переменной, заданной в качестве аргумента в этом вызове. Выходной параметр перестает существовать при завершении выполнения тела функции. В отличие от параметров значения выходной параметр не должен быть записан (§9.7.2.9).

Следующие правила определенного назначения применяются к выходным параметрам.

Примечание. Правила ссылочных параметров отличаются и описаны в разделе (§9.2.6). конечная заметка

  • Переменная не должна быть определенно назначена, прежде чем она может быть передана в качестве выходного параметра в члене функции или вызове делегата.
  • После обычного завершения вызова элемента функции или делегата каждая переменная, переданная в качестве выходного параметра, считается назначена в этом пути выполнения.
  • В члене функции или анонимной функции выходной параметр считается изначально неназначимым.
  • Каждый выходной параметр члена функции, анонимной функции или локальной функции должен быть определенно назначен (§9.4) перед тем, как член функции, анонимная функция или локальная функция обычно возвращается.

Выходные параметры рассматриваются далее в разделе §15.6.2.3.4.

Входные параметры 9.2.8

Входной параметр является ссылочной переменной (§9.7), которая возникает при вызове члена функции, делегата, анонимной функции или локальной функции, а ее референт инициализируется в variable_reference в качестве аргумента в этом вызове. Входной параметр перестает существовать при завершении выполнения текста функции. В отличие от параметров значения входной параметр не должен быть записан (§9.7.2.9).

Следующие определенные правила назначения применяются к входным параметрам.

  • Переменная должна быть определенно назначена (§9.4), прежде чем она может быть передана в качестве входного параметра в члене функции или вызове делегата.
  • В члене функции, анонимной функции или локальной функции входной параметр считается изначально назначенным.

Входные параметры рассматриваются далее в разделе §15.6.2.3.2.

Локальные переменные 9.2.9

Локальная переменная объявляется local_variable_declaration, declaration_expression, foreach_statement или specific_catch_clause try_statement. Локальная переменная также может быть объявлена определенными типами шаблонов (§11). Для foreach_statement локальная переменная является итерацией (§13.9.5). Для specific_catch_clause локальная переменная является переменной исключения (§13.11). Локальная переменная, объявленная foreach_statement или specific_catch_clause , считается первоначально назначенной.

Local_variable_declaration может происходить в блоке, for_statement, switch_block или using_statement. Declaration_expression может возникать как out argument_value и как tuple_element, которая является целью деконструкционного назначения (§12.21.2).

Время существования локальной переменной — это часть выполнения программы, в течение которой хранилище гарантированно зарезервировано для него. Это время существования расширяется от входа в область, с которой она связана, по крайней мере до тех пор, пока выполнение этой области не закончится каким-то образом. (Ввод закрытого блока, вызов метода или получение значения из блока итератора приостановлено, но не заканчивается, выполнение текущей области.) Если локальная переменная захватывается анонимной функцией (§12.19.6.2), ее время существования продолжается по крайней мере до тех пор, пока дерево делегатов или выражений, созданное из анонимной функции, а также любые другие объекты, которые приходят для ссылки на захваченную переменную, имеют право на сборку мусора. Если родительская область введена рекурсивно или итеративно, каждый раз создается новый экземпляр локальной переменной, а его инициализатор , если таковой имеется, вычисляется каждый раз.

Примечание. Локальная переменная создается при каждом вводе области. Это поведение видно для пользовательского кода, содержащего анонимные методы. конечная заметка

Примечание. Время существования переменной итерации (§13.9.5), объявленной foreach_statement, является одной итерацией этой инструкции. Каждая итерация создает новую переменную. конечная заметка

Примечание. Фактическое время существования локальной переменной зависит от реализации. Например, компилятор может статически определить, что локальная переменная в блоке используется только для небольшой части этого блока. С помощью этого анализа компилятор может создать код, который приводит к тому, что хранилище переменной имеет более короткое время существования, чем его содержащий блок.

Хранилище, на которое ссылается локальная ссылочная переменная, освобождается независимо от времени существования этой локальной эталонной переменной (§7.9).

конечная заметка

Локальная переменная, представленная local_variable_declaration или declaration_expression , не инициализирована автоматически и поэтому не имеет значения по умолчанию. Такая локальная переменная считается изначально неназначаемой.

Примечание. Local_variable_declaration, включающая инициализатор, по-прежнему не назначена. Выполнение объявления ведет себя точно так же, как назначение переменной (§9.4.4.5). Использование переменной перед выполнением инициализатора; Например, в самом выражении инициализатора или с помощью goto_statement , которая проходит инициализатор, является ошибкой во время компиляции:

goto L;

int x = 1; // never executed

L: x += 1; // error: x not definitely assigned

В пределах локальной переменной это ошибка во время компиляции, которая ссылается на эту локальную переменную в текстовой позиции, которая предшествует декларатору.

конечная заметка

Отмена 9.2.9.1

Отмена — это локальная переменная, которая не имеет имени. Отклонение представлено выражением объявления (§12.17) с идентификатором_; и является неявно типизированным (_или) или var _явно типизированным (T _).

Примечание._ Является допустимым идентификатором во многих формах объявлений. конечная заметка

Поскольку отмена не имеет имени, единственная ссылка на переменную, представляющая ее, является выражением, которое представляет его.

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

Отмена изначально не назначается, поэтому для доступа к его значению всегда возникает ошибка.

Пример:

_ = "Hello".Length;
(int, int, int) M(out int i1, out int i2, out int i3) { ... }
(int _, var _, _) = M(out int _, out var _, out _);

В примере предполагается, что в области нет объявления имени _ .

Назначение, показывающее _ простой шаблон для пропуска результата выражения. Вызов M показывает различные формы отмены, доступные в кортежах и в качестве выходных параметров.

пример конца

Значения по умолчанию 9.3

Следующие категории переменных автоматически инициализированы в значения по умолчанию:

  • Статические переменные.
  • Переменные экземпляров класса.
  • Элементы массива.

Значение по умолчанию переменной зависит от типа переменной и определяется следующим образом:

  • Для переменной value_type значение по умолчанию совпадает со значением, вычисляемым конструктором по умолчанию value_type (§8.3.3).
  • Для переменной reference_type значение по умолчанию равно null.

Примечание. Инициализация значений по умолчанию обычно выполняется путем инициализации памяти диспетчера памяти или сборщика мусора для инициализации памяти на все биты-ноль, прежде чем он будет выделен для использования. По этой причине удобно использовать all-bits-zero для представления ссылки null. конечная заметка

9.4 Определенное назначение

9.4.1 Общие

В заданном расположении в исполняемом коде члена функции или анонимной функции переменная, как сообщается, определенно назначается, если компилятор может доказать определенный статический анализ потока (§9.4.4), что переменная была автоматически инициализирована или была мишенью по крайней мере одного назначения.

Примечание. Неофициально указано, что правила определенного назначения:

  • Изначально назначенная переменная (§9.4.2) всегда считается определенно назначенной.
  • Изначально неназначаемая переменная (§9.4.3) считается определенно назначенной по указанному расположению, если все возможные пути выполнения, ведущие к данному расположению, содержат по крайней мере одно из следующих:
    • Простое задание (§12.21.2), в котором переменная является левой операндом.
    • Выражение вызова (§12.8.9) или выражение создания объекта (§12.8.16.2), которое передает переменную в качестве выходного параметра.
    • Для локальной переменной объявление локальной переменной для переменной (§13.6.2), которая включает инициализатор переменной.

Официальная спецификация, лежащая в основе указанных выше неофициальных правил, описана в разделе §9.4.2, §9.4.3 и §9.4.4.

конечная заметка

Состояния определенного назначения переменных экземпляра переменной struct_type отслеживаются по отдельности, а также совместно. В дополнение к правилам, описанным в §9.4.2, §9.4.3 и §9.4.4, следующие правила применяются к переменным struct_type и их переменным экземпляра:

  • Переменная экземпляра считается определенно назначена, если ее содержащая struct_type переменная считается определенно назначенной.
  • Переменная struct_type считается определенно назначенной, если каждая из его переменных экземпляра считается определенно назначенной.

Определенное назначение является требованием в следующих контекстах:

  • Переменная должна быть определенно назначена в каждом расположении, где получается его значение.

    Примечание. Это гарантирует, что неопределенные значения никогда не происходят. конечная заметка

    Вхождение переменной в выражении считается получением значения переменной, за исключением случаев, когда

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

    Примечание. Это гарантирует, что вызывающий элемент функции может рассмотреть исходный параметр ссылки. конечная заметка

  • Переменная должна быть определенно назначена в каждом расположении, где она передается в качестве входного параметра.

    Примечание. Это гарантирует, что вызывающий элемент функции может рассмотреть входной параметр, изначально назначенный. конечная заметка

  • Все выходные параметры элемента функции должны быть определенно назначены в каждом расположении, где возвращается член функции (через инструкцию возврата или путем выполнения до конца тела элемента функции).

    Примечание. Это гарантирует, что члены функции не возвращают неопределенные значения в выходных параметрах, что позволяет компилятору рассматривать вызов члена функции, который принимает переменную в качестве выходного параметра, эквивалентного назначению переменной переменной. конечная заметка

  • Переменная this конструктора экземпляра struct_type должна быть определенно назначена в каждом расположении, где возвращается конструктор экземпляра.

9.4.2 Изначально назначенные переменные

Следующие категории переменных классифицируются как первоначально назначенные:

  • Статические переменные.
  • Переменные экземпляров класса.
  • Переменные экземпляра изначально назначенных переменных структуры.
  • Элементы массива.
  • Параметры значения.
  • Ссылочные параметры.
  • Входные параметры.
  • Переменные, объявленные в предложении catch или инструкции foreach .

9.4.3 Изначально неназначенные переменные

Следующие категории переменных классифицируются как первоначально неназначенные:

  • Переменные экземпляра изначально неназначенных переменных структуры.
  • Выходные параметры, включая this переменную конструкторов экземпляров структуры без инициализатора конструктора.
  • Локальные переменные, за исключением объявленных в предложении catch или инструкции foreach .

9.4.4 Точные правила определения определенного назначения

9.4.4.1 Общие

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

Компилятор обрабатывает текст каждого элемента функции, имеющего одну или несколько неназначенных переменных. Для каждой первоначально неназначаемой переменной v компилятор определяет определенное состояние назначения для v на каждом из следующих точек в члене функции:

  • В начале каждой инструкции
  • В конечной точке (§13.2) каждой инструкции
  • На каждой дуге, которая передает управление другому оператору или конечной точке оператора
  • В начале каждого выражения
  • В конце каждого выражения

Состояние определенного назначения v может быть следующим:

  • Определенно назначено. Это означает, что на всех возможных потоках управления до этой точки v назначено значение.
  • Определенно не назначено. Для состояния переменной в конце выражения типа boolсостояние переменной, которая определенно не назначена (но не обязательно) попадает в одно из следующих вложенных состояний:
    • Определенно назначено после истинного выражения. Это состояние указывает, что v определенно назначается, если логическое выражение вычисляется как true, но не обязательно назначается, если логическое выражение вычисляется как false.
    • Определенно назначено после ложного выражения. Это состояние указывает, что v определенно назначается, если логическое выражение вычисляется как false, но не обязательно назначается, если логическое выражение вычисляется как true.

Следующие правила определяют, как определяется состояние переменной v в каждом расположении.

9.4.4.2 Общие правила для инструкций

  • V определенно не назначается в начале тела члена функции.
  • Состояние определенного назначения v в начале любого другого оператора определяется путем проверки состояния определенного назначения v во всех потоках управления, предназначенных для начала этой инструкции. Если (и только если) v определенно назначается для всех таких передач потока управления, то v определенно назначается в начале инструкции. Набор возможных передач потока управления определяется так же, как и для обеспечения доступности инструкций проверки (§13.2).
  • Состояние определенного назначения v в конечной точке конечной точки , checked, uncheckedwhileforeachifusingfordolockили switch оператор определяется путем проверки состояния определенного blockназначения v на всех потоках управления, которые предназначены для конечной точки этого оператора. Если v определенно назначен для всех таких передач потока управления, то v определенно назначается в конечной точке инструкции. В противном случае v определенно не назначается в конечной точке инструкции. Набор возможных передач потока управления определяется так же, как и для обеспечения доступности инструкций проверки (§13.2).

Примечание. Поскольку нет путей управления к неустранимой инструкции, v определенно назначается в начале любой недоступной инструкции. конечная заметка

9.4.4.3 Блокируемые операторы, проверенные и снятые инструкции

Определенное состояние назначения v в элементе управления передается первому оператору списка инструкций в блоке (или в конечную точку блока, если список инструкций пуст) совпадает с оператором определенного назначения v перед блоком checkedили unchecked оператором.

Операторы выражений 9.4.4.4

Для stmt оператора выражения, состоящего из выражения expr:

  • v имеет то же состояние определенного назначения в начале expr , что и в начале stmt.
  • Если v, если определенно назначено в конце экспра, он определенно назначается в конечной точке stmt; в противном случае он не определенно назначен в конечной точке stmt.

Операторы объявления 9.4.4.5

  • Если stmt является оператором объявления без инициализаторов, то v имеет то же состояние определенного назначения в конечной точке stmt, что и в начале stmt.
  • Если stmt является оператором объявления с инициализаторами, то состояние определенного назначения для v определяется как если бы stmt было списком инструкций, с одной инструкцией назначения для каждого объявления с инициализатором (в порядке объявления).

9.4.4.6 Если операторы

Для инструкции stmt формы:

if ( «expr» ) «then_stmt» else «else_stmt»
  • v имеет то же состояние определенного назначения в начале expr , что и в начале stmt.
  • Если v определенно назначен в конце экспра, то он определенно назначается на передачу потока управления на then_stmt и на else_stmt или в конечную точку stmt, если нет другого предложения.
  • Если v имеет состояние "определенно назначено после истинного выражения" в конце экспра, то оно определенно назначается передаче потока управления в then_stmt, а не определенно назначено на передачу потока управления либо else_stmt, либо к конечной точке stmt, если нет другого предложения.
  • Если v имеет состояние "определенно назначено после ложного выражения" в конце экспра, то оно определенно назначается для передачи потока управления в else_stmt, а не определенно назначено на передачу потока управления в then_stmt. Он определенно назначается в конечной точке stmt, если и только если он определенно назначен в конечной точке then_stmt.
  • В противном случае v считается не обязательно назначенным для передачи потока управления в then_stmt или else_stmt или в конечную точку stmt, если нет другого предложения.

Операторы коммутатора 9.4.4.7

Для инструкции switch stmt с управляемым выражением expr:

Определенное состояние назначения v в начале expr совпадает с состоянием v в начале stmt.

Определенное состояние назначения v в начале предложения охранника дела

  • Если v является переменной шаблона, объявленной в switch_label: "определенно назначено".
  • Если метка переключателя, содержащая это предложение guard (§13.8.3), недоступно: "определенно назначено".
  • В противном случае состояние v совпадает с состоянием v после expr.

Пример. Второе правило устраняет необходимость в том, чтобы компилятор выдает ошибку, если к неназначаемой переменной обращается неустранимый код. Состояние b "определенно назначено" в неуправляемой метке case 2 when bпереключателя.

bool b;
switch (1) 
{
    case 2 when b: // b is definitely assigned here.
    break;
}

пример конца

Определенное состояние назначения v в потоке управления передается в список доступных блок-инструкций коммутатора

  • Если передача элемента управления была вызвана оператором goto case или goto default, то состояние v совпадает с состоянием в начале этого оператора goto.
  • Если передача элемента управления была вызвана default меткой переключателя, состояние v совпадает с состоянием v после экспра.
  • Если передача элемента управления возникла из-за неуправляемой метки переключателя, состояние v "определенно назначено".
  • Если передача элемента управления была вызвана меткой доступных переключателей с предложением guard, состояние v совпадает с состоянием v после предложения guard.
  • Если передача элемента управления была вызвана меткой доступных коммутаторов без предложения guard, то состояние v
    • Если v является переменной шаблона, объявленной в switch_label: "определенно назначено".
    • В противном случае состояние v совпадает со статистикой v после expr.

Следствием этих правил является то, что переменная шаблона, объявленная в switch_label , будет "не определенно назначена" в инструкциях раздела коммутатора, если она не является единственной доступной меткой коммутатора в своем разделе.

Пример:

public static double ComputeArea(object shape)
{
    switch (shape)
    {
        case Square s when s.Side == 0:
        case Circle c when c.Radius == 0:
        case Triangle t when t.Base == 0 || t.Height == 0:
        case Rectangle r when r.Length == 0 || r.Height == 0:
            // none of s, c, t, or r is definitely assigned
            return 0;
        case Square s:
            // s is definitely assigned
            return s.Side * s.Side;
        case Circle c:
            // c is definitely assigned
            return c.Radius * c.Radius * Math.PI;
           …
    }
}

пример конца

9.4.4.8 В то время как операторы

Для инструкции stmt формы:

while ( «expr» ) «while_body»
  • v имеет то же состояние определенного назначения в начале expr , что и в начале stmt.
  • Если v определенно назначен в конце экспра, то он определенно назначается на поток управления , передаваемый в while_body и в конечную точку stmt.
  • Если v имеет состояние "определенно назначено после истинного выражения" в конце expr, то оно определенно назначается на передачу потока управления в while_body, но не определенно назначено в конечной точке stmt.
  • Если v имеет состояние "определенно назначено после ложного выражения" в конце экспра, то оно определенно назначается для потока управления, передаваемого в конечную точку stmt, но не определенно назначено для передачи потока управления в while_body.

Операторы Do 9.4.4.9

Для инструкции stmt формы:

do «do_body» while ( «expr» ) ;
  • v имеет то же состояние определенного назначения для передачи потока управления с начала stmt в do_body, как в начале stmt.
  • v имеет то же состояние определенного назначения в начале экспра, что и в конечной точке do_body.
  • Если v определенно назначен в конце экспра, то он определенно назначается на поток управления, передаваемый в конечную точку stmt.
  • Если v имеет состояние "определенно назначено после ложного выражения" в конце экспра, то он определенно назначается на конечную точку stmt, но не определенно назначен на передачу потока управления в do_body.

9.4.4.10 Для операторов

Для инструкции формы:

for ( «for_initializer» ; «for_condition» ; «for_iterator» )
    «embedded_statement»

Проверка определенного назначения выполняется так, как если бы инструкция была написана:

{
    «for_initializer» ;
    while ( «for_condition» )
    {
        «embedded_statement» ;
        LLoop: «for_iterator» ;
    }
}

с continue операторами, предназначенными для инструкций for , которые переводятся в goto инструкции, предназначенные для метки LLoop. Если for_condition опущен из for инструкции, оценка определенного назначения продолжается, как если бы for_condition были заменены на true в приведенном выше расширении.

9.4.4.11 Прерывание, продолжение и инструкции goto

Состояние определенного назначения v в передаче потока управления, вызванного оператором break, continueили goto оператором совпадает с состоянием определенного назначения v в начале инструкции.

Инструкции 9.4.4.12 Throw

Для инструкции stmt формы:

throw «expr» ;

состояние определенного назначения v в начале expr совпадает с состоянием определенного назначения v в начале stmt.

Операторы return 9.4.4.13

Для инструкции stmt формы:

return «expr» ;
  • Состояние определенного назначения v в начале expr совпадает с состоянием определенного назначения v в начале stmt.
  • Если v является выходным параметром, то оно должно быть определенно назначено:
    • после экспра
    • или в конце finally блока илиfinally try-trycatch--finally заключит инструкцию.return

Для инструкции stmt формы:

return ;
  • Если v является выходным параметром, то оно должно быть определенно назначено:
    • перед stmt
    • или в конце finally блока илиfinally try-trycatch--finally заключит инструкцию.return

9.4.4.14 Инструкции Try-catch

Для инструкции stmt формы:

try «try_block»
catch ( ... ) «catch_block_1»
...
catch ( ... ) «catch_block_n»
  • Состояние определенного назначения v в начале try_block совпадает с состоянием определенного назначения v в начале stmt.
  • Определенное состояние назначения v в начале catch_block_i (для любого i) совпадает с состоянием определенного назначения v в начале stmt.
  • Определенное состояние назначения v в конечной точке stmt определенно назначается, если (и только если) v определенно назначается в конечной точке try_block и каждой catch_block_i (для каждого i от 1 до n).

Инструкции 9.4.4.15 Try-finally

Для инструкции stmt формы:

try «try_block» finally «finally_block»
  • Состояние определенного назначения v в начале try_block совпадает с состоянием определенного назначения v в начале stmt.
  • Состояние определенного назначения v в начале finally_block совпадает с состоянием определенного назначения v в начале stmt.
  • Определенное состояние назначения v в конечной точке stmt определенно назначается, если (и только если) по крайней мере одно из следующих значений имеет значение true:
    • V определенно назначается в конечной точке try_block
    • V определенно назначается в конечной точке finally_block

Если передача потока управления (напримерgoto, оператор) начинается в try_block и заканчивается вне try_block, то v также считается определенно назначенным для передачи потока управления, если v определенно назначен в конечной точке finally_block. (Это не только в том случае, если v определенно назначен по другой причине для передачи потока управления, то он по-прежнему считается определенно назначенным.)

9.4.4.16 Инструкции Try-catch-finally

Для инструкции формы:

try «try_block»
catch ( ... ) «catch_block_1»
...
catch ( ... ) «catch_block_n»
finally «finally_block»

Анализ определенного назначения выполняется так, как если бы инструкция была оператором try-finally , заключающим try-catch оператор:

try
{
    try «try_block»
    catch ( ... ) «catch_block_1»
    ...
    catch ( ... ) «catch_block_n»
}
finally «finally_block»

Пример. В следующем примере показано, как различные блоки инструкции try (§13.11) влияют на определенное назначение.

class A
{
    static void F()
    {
        int i, j;
        try
        {
            goto LABEL;
            // neither i nor j definitely assigned
            i = 1;
            // i definitely assigned
        }
        catch
        {
            // neither i nor j definitely assigned
            i = 3;
            // i definitely assigned
        }
        finally
        {
            // neither i nor j definitely assigned
            j = 5;
            // j definitely assigned
        }
        // i and j definitely assigned
        LABEL: ;
        // j definitely assigned
    }
}

пример конца

Инструкции Foreach 9.4.4.17

Для инструкции stmt формы:

foreach ( «type» «identifier» in «expr» ) «embedded_statement»
  • Определенное состояние назначения v в начале expr совпадает с состоянием v в начале stmt.
  • Определенное состояние назначения v в потоке управления передается в embedded_statement или в конечную точку stmt совпадает с состоянием v в конце экспра.

Инструкции using 9.4.4.18

Для инструкции stmt формы:

using ( «resource_acquisition» ) «embedded_statement»
  • Определенное состояние назначения v в начале resource_acquisition совпадает с состоянием v в начале stmt.
  • Определенное состояние назначения v в потоке управления, передаваемого в embedded_statement , совпадает с состоянием v в конце resource_acquisition.

Операторы блокировки 9.4.4.19

Для инструкции stmt формы:

lock ( «expr» ) «embedded_statement»
  • Определенное состояние назначения v в начале expr совпадает с состоянием v в начале stmt.
  • Определенное состояние назначения v в потоке управления, передаваемом в embedded_statement, совпадает с состоянием v в конце экспра.

Операторы доходности 9.4.4.20

Для инструкции stmt формы:

yield return «expr» ;
  • Определенное состояние назначения v в начале expr совпадает с состоянием v в начале stmt.
  • Определенное состояние назначения v в конце stmt совпадает с состоянием v в конце expr.

Оператор yield break не влияет на состояние определенного назначения.

9.4.4.21 Общие правила для константных выражений

Следующее относится к любому постоянному выражению и принимает приоритет над любыми правилами из следующих разделов, которые могут применяться:

Для константного выражения со значением true:

  • Если v определенно назначен перед выражением, то v определенно назначается после выражения.
  • В противном случае значение v "определенно назначено после ложного выражения" после выражения.

Пример:

int x;
if (true) {}
else
{
    Console.WriteLine(x);
}

пример конца

Для константного выражения со значением false:

  • Если v определенно назначен перед выражением, то v определенно назначается после выражения.
  • В противном случае значение v "определенно назначено после истинного выражения" после выражения.

Пример:

int x;
if (false)
{
    Console.WriteLine(x);
}

пример конца

Для всех других константных выражений состояние определенного назначения v после выражения совпадает с состоянием определенного назначения v перед выражением.

9.4.4.22 Общие правила для простых выражений

Следующее правило применяется к таким типам выражений: литералы (§12.8.2), простые имена (§12.8.4), выражения доступа к членам (§12.8.7), неиндексированные выражения базового доступа (§12.12. 8.14), typeof выражения (§12.8.17), выражения значений по умолчанию (§12.8.20), nameof выражения (§12.8.22) и выражения объявлений (§12.17).

  • Состояние определенного назначения v в конце такого выражения совпадает с состоянием определенного назначения v в начале выражения.

9.4.4.23 Общие правила для выражений с внедренными выражениями

Следующие правила применяются к таким типам выражений: круглые скобки (§12.8.5), выражения кортежа (§12.8.6), выражения доступа к элементам (§12.8.11), базовые выражения доступа с индексированием (§12.8.14), добавочные и декрементные выражения (§12.8.15, §12.9.6), выражения приведения (§12.9.7), унарные+, -, * ~выражения, двоичные<<<>>-*%<=+/, >, >=, , !=is==|&as^ выражения (§12.10, §12.11, §12.12, §12.13), составные выражения назначения (§12.21.4) и выражения (§12.8.19), checked выражения массива и делегата создания (§12.8.16) и await unchecked выражения (§12.9.8).

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

Пример. Двоичный % оператор оценивает левую сторону оператора, а затем справа. Операция индексирования вычисляет индексированное выражение, а затем вычисляет каждое из выражений индекса в порядке слева направо. пример конца

Для выражения expr, который имеет вложенные выражения expr₁, exproxy, ..., exprₓ, вычисляется в этом порядке:

  • Определенное состояние назначения v в начале expr₁ совпадает с состоянием определенного назначения в начале экспра.
  • Определенное состояние назначения v в начале expri (i больше одного) совпадает с состоянием определенного назначения в конце expri₋₁.
  • Состояние определенного назначения v в конце экспра совпадает с состоянием определенного назначения в конце exprₓ.

9.4.4.24 Выражения вызова и выражения создания объектов

Если вызываемый метод является частичным методом, который не реализует объявление частичного метода, или является условным методом, для которого вызов опущен (§22.5.3.2), то состояние определенного назначения v после вызова совпадает с состоянием определенного назначения v перед вызовом. В противном случае применяются следующие правила:

Для выражения вызова из формы:

«primary_expression» ( «arg₁», «arg₂», … , «argₓ» )

или выражение создания объекта формы:

new «type» ( «arg₁», «arg₂», … , «argₓ» )
  • Для выражения вызова определенное состояние назначения v до primary_expression совпадает с состоянием v перед экспром.
  • Для выражения вызова определенное состояние назначения v перед arg₁ совпадает с состоянием v после primary_expression.
  • Для выражения создания объекта определенное состояние назначения v перед arg₁ совпадает с состоянием v до экспра.
  • Для каждого аргумента argi определенное состояние назначения v после argi определяется правилами обычных выражений, игнорируя любые inмодификаторы outили ref модификаторы.
  • Для каждого аргумента argi для любого больше одного, определенное состояние назначения v до argi совпадает с состоянием v после argi₋₁.
  • Если переменная v передается в качестве out аргумента (т. е. аргумент формы "out v") в любом из аргументов, то состояние v после экспра определенно назначено. В противном случае состояние v после экспра совпадает с состоянием v после argₓ.
  • Для инициализаторов массива (§12.8.16.5), инициализаторов объектов (§12.8.16.3), инициализаторов коллекций (§12.8.16.4) и анонимных инициализаторов объектов (§12.8.16.7), состояние определенного назначения определяется расширением, которое эти конструкции определены с точки зрения.

9.4.4.25 Простые выражения назначения

Пусть набор целевых объектов назначения в выражении e определяется следующим образом:

  • Если выражение e является выражением кортежа, целевые объекты назначения в e являются объединением целевых объектов назначения элементов e.
  • В противном случае целевые объекты назначения в e имеют значение e.

Для выражения expr формы:

«expr_lhs» = «expr_rhs»
  • Определенное состояние назначения v до expr_lhs совпадает с состоянием определенного назначения v до экспра.
  • Состояние определенного назначения v до expr_rhs совпадает с состоянием определенного назначения v после expr_lhs.
  • Если v является целевым объектом назначения expr_lhs, то определенное состояние назначения v после экспра определенно назначено. В противном случае, если назначение происходит в конструкторе экземпляра типа структуры, а v — скрытое поле резервного копирования автоматически реализуемого свойства P для создаваемого экземпляра, а свойство, назначающее P , является целевым объектом assigment expr_lhs, то определенное состояние назначения v после экспра определенно назначено. В противном случае состояние определенного назначения v после экспра совпадает с состоянием определенного назначения v после expr_rhs.

Пример. В следующем коде

class A
{
    static void F(int[] arr)
    {
        int x;
        arr[x = 1] = x; // ok
    }
}

Переменная x считается определенно назначенной после arr[x = 1] вычисления в левой части второго простого назначения.

пример конца

9.4.4.26 && expressions

Для выражения expr формы:

«expr_first» && «expr_second»
  • Определенное состояние назначения v до expr_first совпадает с состоянием определенного назначения v до экспра.
  • Определенное состояние назначения v до expr_second определенно назначается, если и только если состояние v после expr_first либо определенно назначено, либо "определенно назначено после истинного выражения". В противном случае оно не определенно назначено.
  • Состояние определенного назначения v после экспра определяется следующими значениями:
    • Если состояние v после expr_first определенно назначено, то состояние v после экспра определенно назначено.
    • В противном случае, если состояние v после expr_second определенно назначено, а состояние v после expr_first "определенно назначено после ложного выражения", то состояние v после expr определенно назначено.
    • В противном случае, если состояние v после expr_second определенно назначено или "определенно назначено после истинного выражения", то состояние v после экспра "определенно назначено после истинного выражения".
    • В противном случае, если состояние v после expr_first "определенно назначено после ложного выражения", а состояние v после expr_second "определенно назначено после ложного выражения", то состояние v после expr "определенно назначено после ложного выражения".
    • В противном случае состояние v после экспра определенно не назначено.

Пример. В следующем коде

class A
{
    static void F(int x, int y)
    {
        int i;
        if (x >= 0 && (i = y) >= 0)
        {
            // i definitely assigned
        }
        else
        {
            // i not definitely assigned
        }
        // i not definitely assigned
    }
}

Переменная i считается определенно назначенной в одной из внедренных инструкций if оператора, но не в другой. if В инструкции в методе Fпеременная i определенно назначается в первой внедренной инструкции, так как выполнение выражения (i = y) всегда предшествует выполнению этой внедренной инструкции. В отличие от этого, переменная i определенно не назначается во втором внедренном операторе, так как x >= 0 может быть проверено значение false, что приводит к отмене назначения переменной i.

пример конца

9.4.4.27 || Выражения

Для выражения expr формы:

«expr_first» || «expr_second»
  • Определенное состояние назначения v до expr_first совпадает с состоянием определенного назначения v до экспра.
  • Определенное состояние назначения v до expr_second определенно назначается, если и только если состояние v после expr_first либо определенно назначено, либо "определенно назначено после истинного выражения". В противном случае оно не определенно назначено.
  • Оператор определенного назначения v после экспра определяется следующими значениями:
    • Если состояние v после expr_first определенно назначено, то состояние v после экспра определенно назначено.
    • В противном случае, если состояние v после expr_second определенно назначено, а состояние v после expr_first "определенно назначено после истинного выражения", то состояние v после expr определенно назначено.
    • В противном случае, если состояние v после expr_second определенно назначено или "определенно назначено после ложного выражения", то состояние v после экспра "определенно назначено после ложного выражения".
    • В противном случае, если состояние v после expr_first "определенно назначено после истинного выражения", а состояние v после expr_ секунды "определенно назначено после истинного выражения", то состояние v после экспра "определенно назначено после истинного выражения".
    • В противном случае состояние v после экспра определенно не назначено.

Пример. В следующем коде

class A
{
    static void G(int x, int y)
    {
        int i;
        if (x >= 0 || (i = y) >= 0)
        {
            // i not definitely assigned
        }
        else
        {
            // i definitely assigned
        }
        // i not definitely assigned
    }
}

Переменная i считается определенно назначенной в одной из внедренных инструкций if оператора, но не в другой. if В инструкции в методе Gпеременная i определенно назначается во второй внедренной инструкции, так как выполнение выражения (i = y) всегда предшествует выполнению этой внедренной инструкции. В отличие от этого, переменная определенно не назначается в первой внедренной инструкции, так как x >= 0 может быть проверено значение true, что приводит к отмене знака переменной.i i

пример конца

9.4.4.28 ! выражения

Для выражения expr формы:

! «expr_operand»
  • Состояние определенного назначения v до expr_operand совпадает с состоянием определенного назначения v до экспра.
  • Состояние определенного назначения v после экспра определяется следующими значениями:
    • Если состояние v после expr_operand определенно назначено, то определенно назначается состояние v после экспра .
    • В противном случае, если состояние после expr_operand "определенно назначено после ложного v выражения", то состояние после экспра "определенно назначено после истинного v выражения".
    • В противном случае, если состояние после expr_operand "определенно назначено после истинного v выражения", то состояние v после экспра "определенно назначено после ложного выражения".
    • В противном случае состояние v после экспра определенно не назначено.

9.4.4.29 ?? выражения

Для выражения expr формы:

«expr_first» ?? «expr_second»
  • Определенное состояние назначения v до expr_first совпадает с состоянием определенного назначения v до экспра.
  • Состояние определенного назначения v до expr_second совпадает с состоянием определенного назначения v после expr_first.
  • Оператор определенного назначения v после экспра определяется следующими значениями:
    • Если expr_first является константным выражением (§12.23) со значениемnull, то состояние v после экспра совпадает с состоянием v после expr_second.
    • В противном случае состояние v после экспра совпадает с состоянием определенного назначения v после expr_first.

9.4.4.30 ?: выражения

Для выражения expr формы:

«expr_cond» ? «expr_true» : «expr_false»
  • Определенное состояние назначения v до expr_cond совпадает с состоянием v до экспра.
  • Определенное состояние назначения v до expr_true определенно назначается, если состояние v после expr_cond определенно назначено или "определенно назначено после истинного выражения".
  • Определенное состояние назначения v до expr_false определенно назначается, если состояние v после expr_cond определенно назначено или "определенно назначено после ложного выражения".
  • Состояние определенного назначения v после экспра определяется следующими значениями:
    • Если expr_cond является константным выражением (§12.23) со значениемtrue, состояние v после экспра совпадает с состоянием v после expr_true.
    • В противном случае, если expr_cond является константным выражением (§12.23) со значениемfalse, состояние v после экспра совпадает с состоянием v после expr_false.
    • В противном случае, если состояние v после expr_true определенно назначено и состояние v после expr_false определенно назначено, то состояние v после экспра определенно назначено.
    • В противном случае состояние v после экспра определенно не назначено.

Анонимные функции 9.4.4.31

Для lambda_expression или anonymous_method_expression expr с текстом (блоком или выражением):

  • Определенное состояние назначения параметра совпадает с параметром именованного метода (§9.2.6, §9.2.7, §9.2.7, §9.2.8).
  • Определенное состояние назначения внешней переменной v перед телом совпадает с состоянием v перед экспром. То есть определенное состояние назначения внешних переменных наследуется от контекста анонимной функции.
  • Определенное состояние назначения внешней переменной v после экспра совпадает с состоянием v до экспра.

Пример: пример

class A
{
    delegate bool Filter(int i);
    void F()
    {
        int max;
        // Error, max is not definitely assigned
        Filter f = (int n) => n < max;
        max = 5;
        DoWork(f);
    }
    void DoWork(Filter f) { ... }
}

создает ошибку во время компиляции, так как максимальное значение не назначается, где объявлена анонимная функция.

пример конца

Пример: пример

class A
{
    delegate void D();
    void F()
    {
        int n;
        D d = () => { n = 1; };
        d();
        // Error, n is not definitely assigned
        Console.WriteLine(n);
    }
}

Также возникает ошибка во время компиляции, так как назначение в анонимной функции не влияет на состояние определенного назначения n за пределами анонимной n функции.

пример конца

9.4.4.32 Вызов выражений

Для выражения expr формы:

throwthrown_expr

  • Определенное состояние назначения v до thrown_expr совпадает с состоянием v до экспра.
  • Определенное состояние назначения v после экспра "определенно назначено".

9.4.4.33 Правила для переменных в локальных функциях

Локальные функции анализируются в контексте родительского метода. Существует два пути потока управления, которые имеют значение для локальных функций: вызовы функций и преобразования делегатов.

Определенное назначение для тела каждой локальной функции определяется отдельно для каждого сайта вызова. При каждом вызове переменные, захваченные локальной функцией, считаются определенно назначенными, если они были определенно назначены в точке вызова. Путь потока управления также существует в локальном теле функции на этом этапе и считается доступным. После вызова локальной функции фиксируемые переменные, которые были определенно назначены на каждой точке управления, покидающей функцию (return операторы, операторы, yield await выражения), считаются определенно назначенными после расположения вызова.

Преобразования делегатов имеют путь потока управления к тексту локальной функции. Захваченные переменные определенно назначаются для тела, если они определенно назначены перед преобразованием. Переменные, назначенные локальной функцией, не считаются назначенными после преобразования.

Примечание. Выше предполагается, что тела повторно анализируются для определенного назначения при каждом вызове локальной функции или преобразовании делегата. Компиляторы не требуются для повторного анализа текста локальной функции при каждом вызове или преобразовании делегата. Реализация должна получить результаты, эквивалентные описанию. конечная заметка

Пример. В следующем примере показано определенное назначение для захваченных переменных в локальных функциях. Если локальная функция считывает захваченную переменную перед его записью, перед вызовом локальной функции необходимо определенно назначить записанную переменную. Локальная функция F1 считывает s без назначения. Это ошибка, если F1 вызывается до s определенного назначения. F2i назначается перед чтением. Его можно вызвать до i определенного назначения. Кроме того, может вызываться после F2 того, F3 как s2 определенно назначается в F2.

void M()
{
    string s;
    int i;
    string s2;
   
    // Error: Use of unassigned local variable s:
    F1();
    // OK, F2 assigns i before reading it.
    F2();
    
    // OK, i is definitely assigned in the body of F2:
    s = i.ToString();
    
    // OK. s is now definitely assigned.
    F1();

    // OK, F3 reads s2, which is definitely assigned in F2.
    F3();

    void F1()
    {
        Console.WriteLine(s);
    }
    
    void F2()
    {
        i = 5;
        // OK. i is definitely assigned.
        Console.WriteLine(i);
        s2 = i.ToString();
    }

    void F3()
    {
        Console.WriteLine(s2);
    }
}

пример конца

9.4.4.34 — выражения шаблонов

Для выражения expr формы:

expr_operand является шаблоном

  • Состояние определенного назначения v до expr_operand совпадает с состоянием определенного назначения v до экспра.
  • Если переменная "v" объявлена в шаблоне, то состояние определенного назначения "v" после экспра "определенно назначается, если значение true".
  • В противном случае определенное состояние назначения "v" после экспра совпадает с определенным состоянием назначения "v" после expr_operand.

Ссылки на переменные 9.5

Variable_reference — это выражение, классифицирующееся как переменная. Variable_reference обозначает расположение хранилища, к которому можно получить доступ как для получения текущего значения, так и для хранения нового значения.

variable_reference
    : expression
    ;

Примечание. В C и C++ variable_reference называется lvalue. конечная заметка

9.6 Атомарность ссылок на переменные

Операции чтения и записи следующих типов данных должны быть атомарными: bool, shortbytecharushortuintsbyteintи floatссылочных типов. Кроме того, операции чтения и записи типов перечислений с базовым типом в предыдущем списке также должны быть атомарными. Считывает и записывает другие типы, включая long, ulongdoubleи , а decimalтакже определяемые пользователем типы, не должны быть атомарными. Помимо функций библиотеки, предназначенных для этой цели, нет гарантии атомарного чтения и изменения записи, например в случае увеличения или уменьшения.

9.7 Ссылочные переменные и возвращается

9.7.1 Общие

Ссылочная переменная — это переменная, которая относится к другой переменной, называемой ссылочной (§9.2.6). Ссылочная переменная — это локальная переменная, объявленная модификатором ref .

Эталонная переменная сохраняет variable_reference (§9.5) для его ссылочного элемента, а не значения его ссылки. При использовании ссылочной переменной, в которой требуется значение ссылки, возвращается значение его ссылочного элемента; аналогичным образом, когда эталонная переменная является целью назначения, она является ссылочной, которой назначается. Переменная, к которой ссылается ссылочная переменная, т. е. хранимая variable_reference для его референта, может быть изменена с помощью назначения ссылок (= ref).

Пример. В следующем примере показана локальная ссылочная переменная, ссылка которой является элементом массива:

public class C
{
    public void M()
    {
        int[] arr = new int[10];
        // element is a reference variable that refers to arr[5]
        ref int element = ref arr[5];
        element += 5; // arr[5] has been incremented by 5
    }     
}

пример конца

Возврат ссылки — это variable_reference, возвращенный методом return-by-ref (§15.6.1). Этот variable_reference является ссылкой возвращаемой ссылки.

Пример. В следующем примере демонстрируется возврат ссылки, референт которого является элементом поля массива:

public class C
{
    private int[] arr = new int[10];

    public ref readonly int M()
    {
        // element is a reference variable that refers to arr[5]
        ref int element = ref arr[5];
        return ref element; // return reference to arr[5];
    }     
}

пример конца

Безопасные контексты ссылок 9.7.2

9.7.2.1 Общие

Все ссылочные переменные подчиняются правилам безопасности, которые гарантируют, что контекст ссылочной переменной не больше контекста ссылочной ссылки.

Примечание. Связанное понятие безопасного контекста определяется в разделе (§16.4.12), а также связанные ограничения. конечная заметка

Для любой переменной ref-safe-context этой переменной является контекст, в котором допустима variable_reference (§9.5) этой переменной. Референт ссылочной переменной должен иметь ref-safe-context, который по крайней мере как ref-safe-context самой эталонной переменной.

Примечание. Компилятор определяет ref-safe-context с помощью статического анализа текста программы. Контекст ref-safe-отражает время существования переменной во время выполнения. конечная заметка

Существует три ref-safe-contexts:

  • объявление-блок: контекст ref-safe-context variable_reference локальной переменной (§9.2.9) заключается в том, что область локальной переменной (§13.6.2), включая все вложенные внедренные инструкциив этой области.

    Variable_reference к локальной переменной является допустимым референтом для эталонной переменной, только если ссылочная переменная объявлена в контексте ref-safe-context этой переменной.

  • function-member: в функции variable_reference к любому из следующих элементов имеется ref-safe-context элемента функции:

    • Параметры значения (§15.6.2.2) в объявлении члена функции, включая неявные this функции-члены класса; и
    • Неявный параметр ссылки () (ref§15.6.2.3.3) this функции-члена структуры вместе с его полями.

    Variable_reference с ref-safe-context элемента функции является допустимым референтом только в том случае, если ссылочная переменная объявлена в том же элементе функции.

  • вызывающий контекст: в функции variable_reference к любому из следующих элементов имеется контекст ref-safe-context вызывающего контекста:

    • Ссылочные параметры (§9.2.6), отличные от неявной this функции элемента структуры;
    • Поля-члены и элементы таких параметров;
    • Поля-члены параметров типа класса; и
    • Элементы параметров типа массива.

Variable_reference с ref-safe-context вызывающего контекста может быть ссылкой возвращаемой ссылки.

Эти значения образуют связь вложения от самого узкого (объявления-блока) к самому широкому (вызывающий контекст). Каждый вложенный блок представляет другой контекст.

Пример: в следующем коде показаны примеры различных контекстов ref-safe-contexts. Объявления показывают контекст ref-safe-context для ссылки, который будет инициализацией выражения для переменной ref . В примерах показан контекст ref-safe-context для возвращаемой ссылки:

public class C
{
    // ref safe context of arr is "caller-context". 
    // ref safe context of arr[i] is "caller-context".
    private int[] arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 

    // ref safe context is "caller-context"
    public ref int M1(ref int r1)
    {
        return ref r1; // r1 is safe to ref return
    }

    // ref safe context is "function-member"
    public ref int M2(int v1)
    {
        return ref v1; // error: v1 isn't safe to ref return
    }

    public ref int M3()
    {
        int v2 = 5;

        return ref arr[v2]; // arr[v2] is safe to ref return
    }

    public void M4(int p) 
    {
        int v3 = 6;

        // context of r2 is declaration-block,
        // ref safe context of p is function-member
        ref int r2 = ref p;

        // context of r3 is declaration-block,
        // ref safe context of v3 is declaration-block
        ref int r3 = ref v3;

        // context of r4 is declaration-block,
        // ref safe context of arr[v3] is caller-context
        ref int r4 = ref arr[v3]; 
    }
}

пример конца.

Пример. Для struct типов неявный this параметр передается в качестве ссылочного параметра. Контекст ref-safe-context полей struct типа в качестве элемента-функции предотвращает возвращение этих полей по ссылке. Это правило предотвращает следующий код:

public struct S
{
     private int n;

     // Disallowed: returning ref of a field.
     public ref int GetN() => ref n;
}

class Test
{
    public ref int M()
    {
        S s = new S();
        ref int numRef = ref s.GetN();
        return ref numRef; // reference to local variable 'numRef' returned
    }
}

пример конца.

9.7.2.2 Локальный контекст ссылки на локальные переменные

Для локальной переменной v:

  • Если v это ссылочная переменная, его контекст ref-safe-context совпадает с контекстом ref-safe-context его инициализации.
  • В противном случае его ref-safe-context — это блок объявления.

Безопасный контекст ссылки параметров 9.7.2.3

Для параметра p:

  • Если p это ссылочный или входной параметр, его ref-safe-context является вызывающим контекстом. Если p это входной параметр, он не может быть возвращен как записываемый ref , но может быть возвращен как ref readonly.
  • Если p это выходной параметр, его ref-safe-context является вызывающим контекстом.
  • В противном случае, если p параметр this типа структуры, его ref-safe-context является членом-функцией.
  • В противном случае параметр является параметром значения, а его ref-safe-context является членом-функцией.

Безопасный контекст ссылки на поле 9.7.2.4

Для переменной, указывающей ссылку на поле, : e.F

  • Если e имеет ссылочный тип, его ref-safe-context является вызывающим контекстом.
  • В противном случае, если e имеет тип значения, его ref-safe-context совпадает с контекстом ref-safe-context e.

Операторы 9.7.2.5

Условный оператор (§12.18) и оператор = ref e назначения ссылок (§12.21.1) имеют ссылочные переменные в качестве операндов и дают эталонную переменную. c ? ref e1 : ref e2 Для этих операторов ref-safe-context результата является самым узким контекстом среди ссылок безопасных контекстов всех ref операндов.

Вызов функции 9.7.2.6

Для переменной c , полученной из вызова функции ref-returning, его контекст ref-safe-context является самым узким из следующих контекстов:

  • Вызывающий контекст.
  • ref-safe-context всех refвыражений , outи in аргументов (за исключением приемника).
  • Для каждого входного параметра, если имеется соответствующее выражение, которое является переменной, и существует преобразование удостоверений между типом переменной и типом параметра, ref-safe-context переменной, в противном случае ближайший заключительный контекст.
  • Безопасный контекст (§16.4.12) всех выражений аргументов (включая приемник).

Пример: последний маркер необходим для обработки кода, например

ref int M2()
{
    int v = 5;
    // Not valid.
    // ref safe context of "v" is block.
    // Therefore, ref safe context of the return value of M() is block.
    return ref M(ref v);
}

ref int M(ref int p)
{
    return ref p;
}

пример конца

Вызов свойства и вызов индексатора ( get или set) рассматриваются как вызов функции базового метода доступа по приведенным выше правилам. Вызов локальной функции — это вызов функции.

Значения 9.7.2.7

Контекст ref-safe-context значения является ближайшим вложенным контекстом.

Примечание. Это происходит в вызове, например M(ref d.Length) , где d имеет тип dynamic. Он также согласуется с аргументами, соответствующими входным параметрам. конечная заметка

Вызовы конструктора 9.7.2.8

new Выражение, вызывающее конструктор, подчиняется тем же правилам, что и вызов метода (§9.7.2.6), который считается возвращающим создаваемый тип.

Ограничения 9.7.2.9 для ссылочных переменных

  • Ни ссылочный параметр, ни выходной параметр, ни входной ref параметр, ни локальный, ни локальный или локальный ref struct тип не должны быть записаны лямбда-выражением или локальной функцией.
  • Ни ссылочный параметр, ни выходной параметр, ни входной параметр, ни параметр ref struct типа не должны быть аргументом для метода итератора или async метода.
  • Ни локальный ref , ни локальный ref struct тип не должны находиться в контексте в точке yield return оператора или await выражения.
  • Для переназначения e1 = ref e2ссылок контекст ref-safe-of e2 должен быть по крайней мере таким же широким контекстом, как контекст ref-safe-contexte1.
  • Для инструкции return ref e1возврата ссылок контекст ref-safe-context e1 должен быть вызывающим контекстом.