13 Инструкции
13.1 Общие
C# предоставляет различные инструкции.
Примечание. Большинство этих инструкций будут знакомы разработчикам, которые запрограммировали в C и C++. конечная заметка
statement
: labeled_statement
| declaration_statement
| embedded_statement
;
embedded_statement
: block
| empty_statement
| expression_statement
| selection_statement
| iteration_statement
| jump_statement
| try_statement
| checked_statement
| unchecked_statement
| lock_statement
| using_statement
| yield_statement
| unsafe_statement // unsafe code support
| fixed_statement // unsafe code support
;
unsafe_statement (§23.2) и fixed_statement (§23.7) доступны только в небезопасном коде (§23).
Embedded_statement нетерминал используется для инструкций, которые отображаются в других инструкциях. Использование embedded_statement вместо инструкции исключает использование операторов объявления и помеченных инструкций в этих контекстах.
Пример: код
void F(bool b) { if (b) int i = 44; }
приводит к ошибке во время компиляции, так как
if
для инструкции требуется embedded_statement , а не оператор для ветвиif
. Если этот код разрешен, то переменнаяi
будет объявлена, но ее нельзя использовать. Обратите внимание, что, поместивi
объявление в блок, пример действителен.пример конца
13.2 Конечные точки и доступность
Каждая инструкция имеет конечную точку. Интуитивно понятным образом конечная точка инструкции — это расположение, которое сразу же следует инструкции. Правила выполнения составных операторов (операторы, содержащие внедренные инструкции) указывают действие, которое выполняется, когда элемент управления достигает конечной точки внедренной инструкции.
Пример. Когда элемент управления достигает конечной точки инструкции в блоке, элемент управления передается в следующую инструкцию в блоке. пример конца
Если заявление может быть достигнуто путем выполнения, заявление, как утверждается , доступно. И наоборот, если нет возможности, что заявление будет выполнено, говорится, что это заявление является недоступным.
Пример. В следующем коде
void F() { Console.WriteLine("reachable"); goto Label; Console.WriteLine("unreachable"); Label: Console.WriteLine("reachable"); }
Второй вызов Console.WriteLine недоступен, так как невозможно выполнить инструкцию.
пример конца
Предупреждение сообщается, если оператор, отличный от throw_statement, блокировки или empty_statement , недоступен. В частности, это не ошибка для того, чтобы оператор был недоступен.
Примечание. Чтобы определить, доступен ли конкретный оператор или конечная точка, компилятор выполняет анализ потока в соответствии с правилами доступности, определенными для каждой инструкции. Анализ потока учитывает значения константных выражений (§12.23), которые управляют поведением операторов, но возможные значения неконстантных выражений не учитываются. Другими словами, в целях анализа потока управления неконстантное выражение заданного типа считается любым возможным значением этого типа.
В примере
void F() { const int i = 1; if (i == 2) Console.WriteLine("unreachable"); }
Логическое выражение инструкции является константным выражением
if
, так как оба операнда==
оператора являются константами. Так как константное выражение вычисляется во время компиляции, создавая значениеfalse
,Console.WriteLine
вызов считается недоступным. Однако, еслиi
значение изменено как локальная переменнаяvoid F() { int i = 1; if (i == 2) Console.WriteLine("reachable"); }
Console.WriteLine
Вызов считается доступным, хотя в действительности он никогда не будет выполнен.конечная заметка
Блокировка члена функции или анонимной функции всегда считается доступной. Последовательно оценивая правила доступности каждого оператора в блоке, можно определить доступность любого данного оператора.
Пример. В следующем коде
void F(int x) { Console.WriteLine("start"); if (x < 0) Console.WriteLine("negative"); }
Доступность второго
Console.WriteLine
определяется следующим образом:
- Первый
Console.WriteLine
оператор выражения доступен, так как блокF
метода доступен (§13.3).- Конечная точка первого
Console.WriteLine
оператора выражения становится доступной, так как эта инструкция может быть достигнута (§13.7 и §13.3).- Оператор
if
доступен, так как конечная точка первогоConsole.WriteLine
оператора выражения достигается (§13.7 и §13.3).Console.WriteLine
Второй оператор выражения доступен, так как логическое выражение инструкцииif
не имеет константного значенияfalse
.пример конца
Существует две ситуации, в которых это ошибка во время компиляции для конечной точки инструкции, которую можно достичь:
switch
Так как оператор не разрешает переход к следующему разделу коммутатора, это ошибка во время компиляции для конечной точки списка инструкций раздела коммутатора, доступного к нему. Если эта ошибка возникает, обычно это указывает наbreak
отсутствие инструкции.Это ошибка во время компиляции для конечной точки блока элемента функции или анонимной функции, которая вычисляет значение, доступное для достижения. Если эта ошибка возникает, обычно это означает, что
return
инструкция отсутствует (§13.10.5).
Блоки 13.3
13.3.1 Общие
С помощью блоков можно использовать несколько операторов в таких контекстах, где ожидается только один оператор.
block
: '{' statement_list? '}'
;
Блок состоит из необязательных statement_list (§13.3.2), заключенного в фигурные скобки. Если список заявлений опущен, блок считается пустым.
Блок может содержать операторы объявления (§13.6). Область локальной переменной или константы, объявленной в блоке, является блоком.
Блок выполняется следующим образом:
- Если блок пуст, элемент управления передается в конечную точку блока.
- Если блок не пуст, элемент управления передается в список инструкций. Когда и если элемент управления достигает конечной точки списка инструкций, элемент управления передается в конечную точку блока.
Список инструкций блока доступен, если сам блок доступен.
Конечная точка блока может быть достигнута, если блок пуст или если конечный пункт списка инструкций доступен.
Блок, содержащий одну или несколько yield
инструкций (§13.15), называется блоком итератора. Блоки итератора используются для реализации элементов функции в качестве итераторов (§15.14). Некоторые дополнительные ограничения применяются к блокам итератора:
- Это ошибка во время компиляции для инструкции,
return
отображаемой в блоке итератора (ноyield return
разрешены операторы). - Это ошибка во время компиляции для блока итератора, содержащего небезопасный контекст (§23.2). Блок итератора всегда определяет безопасный контекст, даже если его объявление вложено в небезопасный контекст.
Список инструкций 13.3.2
Список инструкций состоит из одного или нескольких операторов, написанных в последовательности. Списки инструкций происходят в блоках(§13.3) и в switch_blocks (§13.8.3).
statement_list
: statement+
;
Список инструкций выполняется путем передачи элемента управления в первую инструкцию. Когда и если элемент управления достигает конечной точки инструкции, элемент управления передается в следующую инструкцию. Когда и если элемент управления достигает конечной точки последней инструкции, элемент управления передается в конечную точку списка инструкций.
Оператор в списке инструкций доступен, если по крайней мере одно из следующих значений имеет значение true:
- Оператор является первым оператором, и сам список инструкций доступен.
- Конечная точка предыдущей инструкции может быть достигнута.
- Оператор — это помеченная инструкция, и метка ссылается на доступную
goto
инструкцию.
Конечная точка списка инструкций может быть достигнута, если конечный пункт последней инструкции в списке доступен.
13.4 Пустой оператор
Empty_statement ничего не делает.
empty_statement
: ';'
;
Пустой оператор используется при отсутствии операций в контексте, в котором требуется оператор.
Выполнение пустой инструкции просто передает контроль в конечную точку инструкции. Таким образом, конечная точка пустого оператора становится доступной, если пустая инструкция недоступна.
Пример: пустой оператор можно использовать при написании инструкции
while
с пустым текстом:bool ProcessMessage() {...} void ProcessMessages() { while (ProcessMessage()) ; }
Кроме того, пустую инструкцию можно использовать для объявления метки непосредственно перед закрытием блока
}
:void F(bool done) { ... if (done) { goto exit; } ... exit: ; }
пример конца
Операторы с метками 13.5
Labeled_statement позволяет оператору префиксировать метку. Операторы с метками разрешены в блоках, но не допускаются как внедренные инструкции.
labeled_statement
: identifier ':' statement
;
Оператор с метками объявляет метку с именем, заданным идентификатором. Область метки — это весь блок, в котором объявлена метка, включая все вложенные блоки. Это ошибка во время компиляции для двух меток с одинаковым именем, чтобы иметь перекрывающиеся области.
Метка может ссылаться на goto
инструкции (§13.10.4) в пределах области метки.
Примечание. Это означает, что
goto
операторы могут передавать управление в блоках и из блоков, но никогда не в блоки. конечная заметка
Метки имеют собственное пространство объявления и не вмешиваются в другие идентификаторы.
Пример: пример
int F(int x) { if (x >= 0) { goto x; } x = -x; x: return x; }
является допустимым и использует имя x как параметр, так и метку.
пример конца
Выполнение помеченной инструкции соответствует точно выполнению инструкции после метки.
В дополнение к доступности, предоставляемой обычным потоком управления, помеченный оператор доступен, если метка ссылается на доступную goto
инструкцию, если goto
оператор не находится внутри try
блока или catch
блока try_statementfinally
, конечный пункт которого недоступен, и помеченная инструкция находится за пределами try_statement.
Операторы объявления 13.6
13.6.1 Общие
Declaration_statement объявляет одну или несколько локальных переменных, одну или несколько локальных констант или локальную функцию. Операторы объявления разрешены в блоках и блоках коммутаторов, но не допускаются как внедренные инструкции.
declaration_statement
: local_variable_declaration ';'
| local_constant_declaration ';'
| local_function_declaration
;
Локальная переменная объявляется с помощью local_variable_declaration (§13.6.2). Локальная константа объявляется с помощью local_constant_declaration (§13.6.3). Локальная функция объявляется с помощью local_function_declaration (§13.6.4).
Объявленные имена вводятся в ближайшее заключающее пространство объявления (§7.3).
Объявления локальных переменных 13.6.2
13.6.2.1 General
Local_variable_declaration объявляет одну или несколько локальных переменных.
local_variable_declaration
: implicitly_typed_local_variable_declaration
| explicitly_typed_local_variable_declaration
| explicitly_typed_ref_local_variable_declaration
;
Неявно типизированные объявления содержат контекстное ключевое слово (§6.4.4), var
что приводит к синтактической неоднозначности между тремя категориями, которые разрешаются следующим образом:
- Если в области нет именованного
var
типа, а входные данные соответствуют implicitly_typed_local_variable_declaration выбрано; - В противном случае, если имя
var
типа находится в области, implicitly_typed_local_variable_declaration не считается возможным совпадением.
В local_variable_declaration каждая переменная представлена декларатором, который является одним из implicitly_typed_local_variable_declarator, explicitly_typed_local_variable_declarator или ref_local_variable_declarator для неявно типизированных, явно типизированных и ссылок на локальные переменные соответственно. Декларатор определяет имя (идентификатор) и начальное значение, если таковые имеются, введенной переменной.
Если в объявлении есть несколько деклараторов, они обрабатываются, включая любые выражения инициализации, в порядке слева направо (§9.4.4.5).
Примечание. Для local_variable_declaration не возникает как for_initializer (§13.9.4) или resource_acquisition (§13.14) этот левый направо порядок эквивалентен каждому декларатору в отдельном local_variable_declaration. Например:
void F() { int x = 1, y, z = x * 2; }
эквивалентно правилу
void F() { int x = 1; int y; int z = x * 2; }
конечная заметка
Значение локальной переменной получается в выражении с помощью simple_name (§12.8.4). Локальная переменная должна быть определенно назначена (§9.4) в каждом расположении, где получается его значение. Каждая локальная переменная, представленная local_variable_declaration , изначально не назначена (§9.4.3). Если декларатор имеет выражение инициализации, введенная локальная переменная классифицируется как назначенная в конце декларатора (§9.4.4.5).
Область локальной переменной, введенной local_variable_declaration , определена следующим образом (§7.7):
- Если объявление происходит как for_initializer, область — это for_initializer, for_condition, for_iterator и embedded_statement (§13.9.4);
- Если объявление происходит как resource_acquisition , область является самым внешним блоком семантического эквивалентного расширения using_statement (§13.14);
- В противном случае область является блоком, в котором происходит объявление.
Это ошибка для ссылки на локальную переменную по имени в текстовой позиции, которая предшествует его декларатору или в любом выражении инициализации в его деклараторе. В пределах локальной переменной это ошибка во время компиляции для объявления другой локальной переменной, локальной функции или константы с тем же именем.
ref-safe-context (§9.7.2) локальной переменной ref является контекстом ref-safe-context его инициализации variable_reference. ref-safe-context для локальных переменных, отличных от ссылок, является блоком объявления.
13.6.2.2 Неявно типизированные объявления локальных переменных
implicitly_typed_local_variable_declaration
: 'var' implicitly_typed_local_variable_declarator
| ref_kind 'var' ref_local_variable_declarator
;
implicitly_typed_local_variable_declarator
: identifier '=' expression
;
В implicitly_typed_local_variable_declaration представлена одна локальная переменная, идентификатор. Выражение или variable_reference должно иметь тип времени компиляции. T
Первая альтернатива объявляет переменную с начальным значением выражения; его тип — это тип T?
ссылки, не допускающий значения NULL, в противном случае — типT
.T
Вторая альтернатива объявляет переменную ссылок с начальным значением ref
variable_reference; его тип — ref T?
это тип, когда T
является ненулевой ссылочным типом, в противном случае — его тип ref T
. (ref_kind описано в разделе §15.6.1.)
Пример:
var i = 5; var s = "Hello"; var d = 1.0; var numbers = new int[] {1, 2, 3}; var orders = new Dictionary<int,Order>(); ref var j = ref i; ref readonly var k = ref i;
Неявно типизированные объявления локальных переменных выше точно эквивалентны следующим явно типизированным объявлениям:
int i = 5; string s = "Hello"; double d = 1.0; int[] numbers = new int[] {1, 2, 3}; Dictionary<int,Order> orders = new Dictionary<int,Order>(); ref int j = ref i; ref readonly int k = ref i;
Ниже приведены неправильные неявно типизированные объявления локальных переменных:
var x; // Error, no initializer to infer type from var y = {1, 2, 3}; // Error, array initializer not permitted var z = null; // Error, null does not have a type var u = x => x + 1; // Error, anonymous functions do not have a type var v = v++; // Error, initializer cannot refer to v itself
пример конца
13.6.2.3 Явно типизированные объявления локальных переменных
explicitly_typed_local_variable_declaration
: type explicitly_typed_local_variable_declarators
;
explicitly_typed_local_variable_declarators
: explicitly_typed_local_variable_declarator
(',' explicitly_typed_local_variable_declarator)*
;
explicitly_typed_local_variable_declarator
: identifier ('=' local_variable_initializer)?
;
local_variable_initializer
: expression
| array_initializer
;
Explicity_typed_local_variable_declaration вводит одну или несколько локальных переменных с указанным типом.
Если local_variable_initializer присутствует, то его тип должен соответствовать правилам простого назначения (§12.21.2) или инициализации массива (§17.7), а его значение назначается в качестве начального значения переменной.
13.6.2.4 Явно типизированные объявления локальных переменных
explicitly_typed_ref_local_variable_declaration
: ref_kind type ref_local_variable_declarators
;
ref_local_variable_declarators
: ref_local_variable_declarator (',' ref_local_variable_declarator)*
;
ref_local_variable_declarator
: identifier '=' 'ref' variable_reference
;
Инициализация variable_reference должна иметь тип типа и соответствовать тем же требованиям, что и для назначения ссылок (§12.21.3).
Если ref_kind, ref readonly
объявленные идентификаторы являются ссылками на переменные, которые обрабатываются как доступные только для чтения. В противном случае, если ref_kind, ref
объявленные идентификаторы являются ссылками на переменные, которые должны быть записываемы.
Это ошибка во время компиляции для объявления локальной переменной ref или переменной ref struct
типа, в методе, объявленном с async
, или внутри итератора (§15.14).
13.6.3 Локальные объявления констант
Local_constant_declaration объявляет одну или несколько локальных констант.
local_constant_declaration
: 'const' type constant_declarators
;
constant_declarators
: constant_declarator (',' constant_declarator)*
;
constant_declarator
: identifier '=' constant_expression
;
Тип local_constant_declaration указывает тип констант, введенных объявлением. За типом следует список constant_declarators, каждый из которых представляет новую константу.
Constant_declarator состоит из идентификатора, который называет константу, а затем маркером "=
" и constant_expression (§12.23), который дает значение константы.
Тип и constant_expression локального объявления констант должны соответствовать тем же правилам, что и объявление константного члена (§15.4).
Значение локальной константы получается в выражении с помощью simple_name (§12.8.4).
Область локальной константы — это блок, в котором происходит объявление. Это ошибка, которая ссылается на локальную константу в текстовой позиции, которая предшествует концу его constant_declarator.
Локальное объявление констант, объявляющее несколько констант, эквивалентно нескольким объявлениям отдельных констант с одинаковым типом.
Объявления локальных функций 13.6.4
Local_function_declaration объявляет локальную функцию.
local_function_declaration
: local_function_modifier* return_type local_function_header
local_function_body
| ref_local_function_modifier* ref_kind ref_return_type
local_function_header ref_local_function_body
;
local_function_header
: identifier '(' parameter_list? ')'
| identifier type_parameter_list '(' parameter_list? ')'
type_parameter_constraints_clause*
;
local_function_modifier
: ref_local_function_modifier
| 'async'
;
ref_local_function_modifier
: 'static'
| unsafe_modifier // unsafe code support
;
local_function_body
: block
| '=>' null_conditional_invocation_expression ';'
| '=>' expression ';'
;
ref_local_function_body
: block
| '=>' 'ref' variable_reference ';'
;
Примечание грамматики. При распознавании local_function_body, если применяются null_conditional_invocation_expression и альтернативные выражения, то следует выбрать его. (§15.6.1)
Пример. Существует два распространенных варианта использования локальных функций: методы итератора и асинхронные методы. В случае методов итератора исключения наблюдаются только при вызове кода, перечисляющего возвращенную последовательность. В асинхронных методах все исключения наблюдаются только при ожидании возвращаемой задачи. В следующем примере показано отделение проверки параметров от реализации итератора с использованием локальной функции:
public static IEnumerable<char> AlphabetSubset(char start, char end) { if (start < 'a' || start > 'z') { throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter"); } if (end < 'a' || end > 'z') { throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter"); } if (end <= start) { throw new ArgumentException( $"{nameof(end)} must be greater than {nameof(start)}"); } return AlphabetSubsetImplementation(); IEnumerable<char> AlphabetSubsetImplementation() { for (var c = start; c < end; c++) { yield return c; } } }
пример конца
Если иное не указано ниже, семантика всех элементов грамматики совпадает с method_declaration (§15.6.1), считывается в контексте локальной функции вместо метода.
Идентификатор local_function_declaration должен быть уникальным в объявленной области блока, включая все вложенные локальные пространства объявлений переменной. Одним из последствий этого является то, что перегруженные local_function_declarationне допускаются.
Local_function_declaration может включать модификатор async
(§15.15) и один unsafe
модификатор (§23.1). Если объявление включает async
модификатор, то возвращаемый тип должен быть void
или «TaskType»
тип (§15.15.1). Если объявление включает static
модификатор, функция является статической локальной функцией; в противном случае это нестатичная локальная функция. Это ошибка во время компиляции для type_parameter_list или parameter_list для хранения атрибутов. Если локальная функция объявлена в небезопасном контексте (§23.2), локальная функция может включать небезопасный код, даже если объявление локальной функции не включает unsafe
модификатор.
Локальная функция объявляется в области блокировки. Нестатичная локальная функция может записывать переменные из включающей области, а статическую локальную функцию не должна (поэтому у нее нет доступа к закрытым локальным, параметрам, не статическим локальным функциям или this
). Это ошибка во время компиляции, если записанная переменная считывается текстом нестатитической локальной функции, но не определенно назначается перед каждым вызовом функции. Компилятор должен определить, какие переменные определенно назначаются при возврате (§9.4.4.33).
Если тип структуры является типом this
структуры, это ошибка во время компиляции для текста локальной функции для доступа this
. Это верно, является ли доступ явным (как в this.x
) или неявным (как в x
том месте, где x
является элементом экземпляра структуры). Это правило запрещает только такой доступ и не влияет на то, приводит ли поиск элементов к члену структуры.
Это ошибка во время компиляции для текста локальной функции, содержащей goto
инструкцию, break
инструкцию или continue
оператор, целевой объект которого находится за пределами тела локальной функции.
Примечание. Приведенные выше правила и
this
goto
зеркальное отображение правил для анонимных функций в §12.19.3. конечная заметка
Локальная функция может вызываться из лексической точки до объявления. Однако это ошибка во время компиляции для функции, которая будет объявлена лексически до объявления переменной, используемой в локальной функции (§7.7).
Это ошибка во время компиляции для локальной функции, чтобы объявить параметр, параметр типа или локальную переменную с тем же именем, что и один, объявленный в любом включающем пространство объявления локальной переменной.
Локальные тела функций всегда доступны. Конечная точка объявления локальной функции становится доступной, если можно достичь начальной точки объявления локальной функции.
Пример. В следующем примере текст доступен,
L
даже если начальная точкаL
недоступно. Так как начальная точкаL
недоступна, оператор, следующий за конечной точкойL
, недоступен:class C { int M() { L(); return 1; // Beginning of L is not reachable int L() { // The body of L is reachable return 2; } // Not reachable, because beginning point of L is not reachable return 3; } }
Другими словами, расположение объявления локальной функции не влияет на доступность каких-либо инструкций в содержащей функции. пример конца
Если тип аргумента для локальной функции имеет значение dynamic
, вызываемая функция должна быть разрешена во время компиляции, а не во время выполнения.
Локальная функция не должна использоваться в дереве выражений.
Статическую локальную функцию
- Может ссылать статические члены, параметры типа, определения констант и статические локальные функции из включающей области.
- Не следует ссылаться
this
на неявнуюbase
ссылку илиthis
ни элементы экземпляра, ни локальные переменные, параметры или нестатические локальные функции из включающей области. Однако все это разрешено вnameof()
выражении.
Операторы выражения 13.7
Expression_statement вычисляет заданное выражение. Значение, вычисленное выражением, если таковой имеется, удаляется.
expression_statement
: statement_expression ';'
;
statement_expression
: null_conditional_invocation_expression
| invocation_expression
| object_creation_expression
| assignment
| post_increment_expression
| post_decrement_expression
| pre_increment_expression
| pre_decrement_expression
| await_expression
;
Не все выражения разрешены в виде инструкций.
Примечание. В частности, выражения, такие как
x + y
иx == 1
, которые просто вычисляют значение (которое будет отменено), не допускаются в качестве инструкций. конечная заметка
Выполнение expression_statement вычисляет содержащееся выражение, а затем передает управление в конечную точку expression_statement. Конечная точка expression_statement достигается, если это expression_statement доступно.
Операторы выбора 13.8
13.8.1 Общие
Операторы выбора выбирают одну из возможных инструкций для выполнения на основе значения некоторого выражения.
selection_statement
: if_statement
| switch_statement
;
13.8.2 Оператор if
Оператор if
выбирает инструкцию для выполнения на основе значения логического выражения.
if_statement
: 'if' '(' boolean_expression ')' embedded_statement
| 'if' '(' boolean_expression ')' embedded_statement
'else' embedded_statement
;
Часть else
связана с лексически ближайшим предыдущим if
, разрешенным синтаксисом.
Пример. Таким образом,
if
оператор формыif (x) if (y) F(); else G();
эквивалентно правилу
if (x) { if (y) { F(); } else { G(); } }
пример конца
Оператор if
выполняется следующим образом:
- Вычисляется boolean_expression (§12.24).
- Если логическое выражение дает
true
, элемент управления передается в первую внедренную инструкцию. Когда и если элемент управления достигает конечной точки этого оператора, элемент управления передается в конечную точку инструкцииif
. - Если логическое выражение дает
false
иelse
если часть присутствует, элемент управления передается во вторую внедренную инструкцию. Когда и если элемент управления достигает конечной точки этого оператора, элемент управления передается в конечную точку инструкцииif
. - Если логическое выражение дает
false
иelse
если часть отсутствует, элемент управления передается в конечную точку инструкцииif
.
Первый внедренный оператор инструкции if
доступен, если if
оператор доступен, и логическое выражение не имеет константного значения false
.
Второй внедренный оператор оператора if
, если он присутствует, доступен, если if
оператор доступен, и логическое выражение не имеет константного значения true
.
Конечная точка инструкции if
может быть достигнута, если конечная точка по крайней мере одной из внедренных инструкций становится доступной. Кроме того, конечная точка if
оператора без else
части недоступна, если if
оператор доступен, и логическое выражение не имеет константного значения true
.
13.8.3 Инструкция switch
Оператор switch
выбирает для выполнения список инструкций, имеющий связанную метку коммутатора, соответствующую значению выражения switch.
switch_statement
: 'switch' '(' expression ')' switch_block
;
switch_block
: '{' switch_section* '}'
;
switch_section
: switch_label+ statement_list
;
switch_label
: 'case' pattern case_guard? ':'
| 'default' ':'
;
case_guard
: 'when' expression
;
Switch_statement состоит из ключевого словаswitch
, за которым следует скобка (называемое выражением switch), а затем switch_block.
Switch_block состоит из нуля или более switch_section, заключенных в фигурные скобки. Каждый switch_section состоит из одного или нескольких switch_label, за которым следует statement_list (§13.3.2). Каждый switch_label , содержащий case
связанный шаблон (§11), по которому проверяется значение выражения коммутатора. Если case_guard присутствует, его выражение должно быть неявно преобразовано в тип bool
, и это выражение оценивается как дополнительное условие для рассмотрения дела.
Руководящий тип инструкции switch
устанавливается выражением switch.
- Если тип выражения переключателя имеет
sbyte
значение ,byte
short
ushort
int
uint
long
ulong
char
bool
string
или enum_type, или если это тип значения NULL, соответствующий одному из этих типов, то это руководящий тип инструкции.switch
- В противном случае, если одно определяемое пользователем неявное преобразование существует из типа выражения коммутатора к одному из следующих возможных типов управления:
sbyte
,byte
short
ushort
int
uint
long
ulong
char
string
или, допускающее значение NULL, соответствующее одному из этих типов, то преобразованный тип является руководящим типом инструкции.switch
- В противном случае тип инструкции
switch
является типом выражения switch. Это ошибка, если такой тип отсутствует.
В инструкции default
может быть не более одной switch
метки.
Это ошибка, если шаблон любой метки коммутатора неприменимо (§11.2.1) к типу входного выражения.
Это ошибка, если шаблон любой метки коммутатора подмечен (§11.3) набор шаблонов более ранних меток коммутатора инструкции switch, у которых нет регистра или чей контроль регистра является константным выражением со значением true.
Пример:
switch (shape) { case var x: break; case var _: // error: pattern subsumed, as previous case always matches break; default: break; // warning: unreachable, all possible values already handled. }
пример конца
Оператор switch
выполняется следующим образом:
- Выражение коммутатора вычисляется и преобразуется в тип управления.
- Элемент управления передается в соответствии со значением преобразованного выражения коммутатора:
- Лексически первый шаблон в наборе
case
меток в той жеswitch
инструкции, которая соответствует значению выражения switch, и для которого выражение guard либо отсутствует, либо оценивается как истинное, так и вызывает передачу элемента управления в список инструкций после соответствующейcase
метки. - В противном случае, если
default
метка присутствует, элемент управления передается в список инструкцийdefault
после метки. - В противном случае элемент управления передается в конечную точку инструкции
switch
.
- Лексически первый шаблон в наборе
Примечание. Порядок сопоставления шаблонов во время выполнения не определен. Компилятор может (но не требуется) сопоставлять шаблоны вне порядка и повторно использовать результаты уже сопоставленных шаблонов для вычисления результата сопоставления других шаблонов. Тем не менее, компилятору требуется определить лексически первый шаблон, соответствующий выражению, и для которого охранное условие либо отсутствует, либо имеет значение
true
. конечная заметка
Если конечная точка списка инструкций раздела коммутатора недоступна, возникает ошибка во время компиляции. Это называется правилом "нет провала".
Пример: пример
switch (i) { case 0: CaseZero(); break; case 1: CaseOne(); break; default: CaseOthers(); break; }
является допустимым, так как раздел переключателя не имеет доступную конечную точку. В отличие от C и C++, выполнение раздела коммутатора не допускается "переходить" к следующему разделу коммутатора, а пример
switch (i) { case 0: CaseZero(); case 1: CaseZeroOrOne(); default: CaseAny(); }
приводит к ошибке во время компиляции. Если за выполнением раздела коммутатора следует выполнить другой раздел коммутатора, следует использовать явный
goto case
илиgoto default
оператор:switch (i) { case 0: CaseZero(); goto case 1; case 1: CaseZeroOrOne(); goto default; default: CaseAny(); break; }
пример конца
В switch_section разрешено несколько меток.
Пример: пример
switch (i) { case 0: CaseZero(); break; case 1: CaseOne(); break; case 2: default: CaseTwo(); break; }
допустимо. В примере не нарушается правило "без падения", так как метки
case 2:
и являются частью одной иdefault:
той же switch_section.пример конца
Примечание. Правило "без падения" предотвращает общий класс ошибок, возникающих в C и C++ при
break
случайном пропуске инструкций. Например, разделы приведеннойswitch
выше инструкции можно отменить, не влияя на поведение инструкции:switch (i) { default: CaseAny(); break; case 1: CaseZeroOrOne(); goto default; case 0: CaseZero(); goto case 1; }
конечная заметка
Примечание. Список инструкций раздела switch обычно заканчивается в
break
goto case
операторе илиgoto default
инструкции, но любая конструкция, которая отображает конечную точку списка инструкций, недоступен. Например, оператор, контролируемый логическим выражениемwhile
, как известно,true
никогда не достигает конечной точки. Аналогичным образом, операторthrow
return
всегда передает контроль в другом месте и никогда не достигает конечной точки. Таким образом, в следующем примере допустимо:switch (i) { case 0: while (true) { F(); } case 1: throw new ArgumentException(); case 2: return; }
конечная заметка
Пример. Тип инструкции
switch
может быть типомstring
. Например:void DoCommand(string command) { switch (command.ToLower()) { case "run": DoRun(); break; case "save": DoSave(); break; case "quit": DoQuit(); break; default: InvalidCommand(command); break; } }
пример конца
Примечание. Как и операторы равенства строк (§12.12.8),
switch
оператор учитывает регистр и будет выполнять данный раздел коммутатора только в том случае, если строка выражения коммутатора точно соответствуетcase
константе метки. конечная заметка, если руководящий тип инструкции являетсяswitch
или типом значенийstring
, допускаемым значением NULL, значениеnull
допускается какcase
константа метки.
Statement_list switch_blockмогут содержать операторы объявления (§13.6). Область локальной переменной или константы, объявленной в блоке коммутатора, является блоком коммутатора.
Метка переключателя может быть доступной, если по крайней мере одно из следующих значений имеет значение true:
- Выражение коммутатора является константным значением и либо
- Метка — это шаблон, который соответствует шаблону
case
(§11.2.1), значение которого, а охранник метки отсутствует или не является константным выражением со значением false; или -
default
это метка, и раздел переключателя не содержит метку регистра, шаблон которой будет соответствовать данному значению, и чей охранник либо отсутствует, либо константное выражение со значением true.
- Метка — это шаблон, который соответствует шаблону
- Выражение коммутатора не является константным значением и либо
- Метка является без охранника или с охранником, значение которого не является
case
константой false; или - это
default
метка и- набор шаблонов, отображаемых среди случаев инструкции switch, которая не имеет охранников или имеют охранников, значение которых является константой true, не является исчерпывающим (§11.4) для типа управления коммутатором; или
- Параметр управления типом является типом, допускающим значение NULL, и набор шаблонов, отображаемых среди случаев инструкции switch, не имеющих охранников или имеющих охранников, значение которых является константой, не содержит шаблон, соответствующий значению
null
.
- Метка является без охранника или с охранником, значение которого не является
- Метка переключателя ссылается на доступную
goto case
илиgoto default
инструкцию.
Список инструкций заданного раздела коммутатора доступен, если switch
оператор доступен, а раздел переключателя содержит метку доступных коммутаторов.
Конечная точка switch
оператора достигается, если оператор switch доступен, и по крайней мере одно из следующих значений имеет значение true:
- Инструкция
switch
содержит доступнуюbreak
инструкцию, которая завершает инструкциюswitch
. - Метка отсутствует
default
и либо- Выражение коммутатора является неконстантным значением, и набор шаблонов, отображаемых среди случаев инструкции switch, не имеющих охранников или охранников, значение которого является константой true, не является исчерпывающим (§11.4) для управляющего типа коммутатора.
- Выражение переключателя — это неконстантное значение типа, допускающего значение NULL, и шаблон не отображается среди случаев оператора switch, у которого нет охранников или есть охранники, значение которого является константой true, соответствует значению
null
. - Выражение коммутатора является константным значением и без
case
метки без охранника или чей охранник является константой true, будет соответствовать такому значению.
Пример. В следующем коде показано краткое использование
when
предложения:static object CreateShape(string shapeDescription) { switch (shapeDescription) { case "circle": return new Circle(2); … case var o when string.IsNullOrWhiteSpace(o): return null; default: return "invalid shape description"; } }
Регистр var совпадает
null
, пустая строка или любая строка, содержащая только пробелы. пример конца
Операторы итерации 13.9
13.9.1 Общие
Операторы итерации многократно выполняют внедренную инструкцию.
iteration_statement
: while_statement
| do_statement
| for_statement
| foreach_statement
;
13.9.2 Оператор while
Оператор while
условно выполняет внедренную инструкцию ноль или более раз.
while_statement
: 'while' '(' boolean_expression ')' embedded_statement
;
Оператор while
выполняется следующим образом:
- Вычисляется boolean_expression (§12.24).
- Если логическое выражение дает
true
, элемент управления передается в внедренную инструкцию. Когда и если элемент управления достигает конечной точки внедренного оператора (возможно, из выполненияcontinue
инструкции), элемент управления передается в начало инструкцииwhile
. - Если логическое выражение дает
false
, элемент управления передается в конечную точку инструкцииwhile
.
В внедренном операторе инструкции while
break
можно использовать оператор (§13.10.2while
таким образом, заканчивая итерацию внедренной инструкции), а continue
оператор (§13.10.3) может использоваться для передачи управления в конечную точку внедренной инструкции (таким образом, выполняя другую итерацию while
инструкции).
Внедренный оператор оператора while
доступен, если while
оператор доступен, и логическое выражение не имеет константного значения false
.
Конечная точка while
оператора может быть достигнута, если по крайней мере одно из следующих значений имеет значение true:
- Инструкция
while
содержит доступнуюbreak
инструкцию, которая завершает инструкциюwhile
. - Оператор
while
доступен, и логическое выражение не имеет константного значенияtrue
.
13.9.3 Инструкция do
Оператор do
условно выполняет внедренную инструкцию один или несколько раз.
do_statement
: 'do' embedded_statement 'while' '(' boolean_expression ')' ';'
;
Оператор do
выполняется следующим образом:
- Элемент управления передается в внедренную инструкцию.
- Когда и если элемент управления достигает конечной точки внедренного оператора (возможно, из выполнения
continue
инструкции), вычисляется boolean_expression (§12.24). Если логическое выражение даетtrue
, элемент управления передается в начало инструкцииdo
. В противном случае элемент управления передается в конечную точку инструкцииdo
.
В внедренном операторе инструкции do
break
можно использовать оператор (§13.10.2do
таким образом, заканчивая итерацию внедренной инструкции), а continue
оператор (§13.10.3) может использоваться для передачи управления в конечную точку внедренной инструкции (таким образом, выполняя другую итерацию do
инструкции).
Внедренный оператор инструкции do
доступен, если оператор do
доступен.
Конечная точка do
оператора может быть достигнута, если по крайней мере одно из следующих значений имеет значение true:
- Инструкция
do
содержит доступнуюbreak
инструкцию, которая завершает инструкциюdo
. - Конечная точка внедренного оператора достигается, и логическое выражение не имеет константного значения
true
.
13.9.4 Инструкция for
Оператор for
вычисляет последовательность выражений инициализации, а затем, пока условие имеет значение true, многократно выполняет внедренную инструкцию и оценивает последовательность выражений итерации.
for_statement
: 'for' '(' for_initializer? ';' for_condition? ';' for_iterator? ')'
embedded_statement
;
for_initializer
: local_variable_declaration
| statement_expression_list
;
for_condition
: boolean_expression
;
for_iterator
: statement_expression_list
;
statement_expression_list
: statement_expression (',' statement_expression)*
;
For_initializer, если они присутствуют, состоят из local_variable_declaration (§13.6.2) или списка statement_expression(§13.7), разделенных запятыми. Область локальной переменной, объявленной for_initializer, — это for_initializer, for_condition, for_iterator и embedded_statement.
For_condition, если они присутствуют, должны быть boolean_expression (§12.24).
For_iterator, если присутствует, состоит из списка statement_expression(§13.7), разделенных запятыми.
Оператор for
выполняется следующим образом:
- Если for_initializer присутствует, инициализаторы переменных или выражения инструкций выполняются в том порядке, в котором они записываются. Этот шаг выполняется только один раз.
- Если for_condition присутствует, она вычисляется.
-
Если for_condition отсутствует или если оценка дает
true
, элемент управления передается в внедренную инструкцию. Когда и если элемент управления достигает конечной точки внедренного оператора (возможно, из выполненияcontinue
инструкции), выражения for_iterator, если таковые имеются, вычисляются последовательно, а затем выполняется другая итерация, начиная с оценки for_condition на шаге выше. -
Если for_condition присутствует и результат
false
оценки, элемент управления передается в конечную точку инструкцииfor
.
В внедренном операторе инструкции for
break
можно использовать оператор (§13.10.2) для передачи управления в конечную точку инструкции (таким образом, заканчивая итерацию внедренной инструкции), а for
оператор (continue
) может использоваться для передачи управления в конечную точку внедренной инструкции (таким образом выполняя for_iterator и выполняя другую итерацию for
инструкции, начиная с for_condition).
Внедренный оператор оператора for
доступен, если одно из следующих значений имеет значение true:
- Оператор
for
доступен, и for_condition отсутствует. - Оператор
for
доступен, и for_condition присутствует и не имеет константного значенияfalse
.
Конечная точка for
оператора может быть достигнута, если по крайней мере одно из следующих значений имеет значение true:
- Инструкция
for
содержит доступнуюbreak
инструкцию, которая завершает инструкциюfor
. - Оператор
for
доступен, и for_condition присутствует и не имеет константного значенияtrue
.
13.9.5 Оператор foreach
Инструкция foreach
перечисляет элементы коллекции, выполняя внедренную инструкцию для каждого элемента коллекции.
foreach_statement
: 'foreach' '(' ref_kind? local_variable_type identifier 'in'
expression ')' embedded_statement
;
Local_variable_type и идентификатор инструкции foreach объявляют переменную итерации инструкции.
var
Если идентификатор присваивается в качестве local_variable_type, а имя типа var
не находится в области, переменная итерации, как говорят, является неявно типизированной переменной итерации, и его тип принимается как тип foreach
элемента инструкции, как указано ниже.
Если foreach_statement содержит оба или ни ref
другого, то readonly
переменная итерации обозначает переменную, которая рассматривается как доступная только для чтения. В противном случае, если foreach_statement содержит ref
без readonly
этого, переменная итерации обозначает переменную, которая должна быть записываема.
Переменная итерации соответствует локальной переменной с областью, которая расширяется по внедренной инструкции. Во время выполнения инструкции foreach
переменная итерации представляет элемент коллекции, для которого выполняется итерация. Если переменная итерации обозначает переменную только для чтения, ошибка во время компиляции возникает, если внедренная инструкция пытается изменить ее (через назначение или ++
--
операторы) или передать ее в качестве ссылочного или выходного параметра.
В следующем примере для краткости IEnumerable
IEnumerator
и IEnumerable<T>
IEnumerator<T>
ссылки на соответствующие типы в пространствах System.Collections
имен и System.Collections.Generic
.
Обработка инструкции foreach
во время компиляции сначала определяет тип коллекции, тип перечислителя и тип итерации выражения. Это определение продолжается следующим образом:
- Если тип выражения является типом
X
массиваSystem.Array
Тип коллекции — это интерфейс, тип перечислителя —IEnumerable
IEnumerator
интерфейс, а тип итерации — это тип элемента типаX
массива. - Если тип
X
выражения есть, то существует неявное преобразование из выражения вdynamic
интерфейс (§10.2.10).IEnumerable
Тип коллекции — интерфейсIEnumerable
, а тип перечислителя —IEnumerator
интерфейс.var
Если идентификатор указан в качестве local_variable_type, то тип итерации имеет значениеdynamic
, в противном случае —object
. - В противном случае определите, имеет ли тип
X
соответствующийGetEnumerator
метод:- Выполните поиск элементов для типа
X
с идентификаторомGetEnumerator
и без аргументов типа. Если поиск элемента не создает совпадение или создает неоднозначность или создает совпадение, которое не является группой методов, проверьте наличие перечисленного интерфейса, как описано ниже. Рекомендуется выпустить предупреждение, если подстановка элемента создает что-либо, кроме группы методов или совпадения. - Выполните разрешение перегрузки с помощью результирующей группы методов и пустого списка аргументов. Если разрешение перегрузки не приводит к применению применимых методов, приводит к неоднозначности или приводит к одному лучшему методу, но этот метод является статическим или не общедоступным, проверьте наличие перечисленного интерфейса, как описано ниже. Рекомендуется выдавать предупреждение, если разрешение перегрузки создает что-либо, кроме однозначного метода общедоступного экземпляра или нет применимых методов.
- Если возвращаемый тип
E
GetEnumerator
метода не является классом, структурой или типом интерфейса, создается ошибка, и дальнейшие действия не выполняются. - Поиск элементов выполняется
E
с идентификаторомCurrent
и без аргументов типа. Если поиск элемента не соответствует, результатом является ошибка, или результатом является что-либо, кроме свойства общедоступного экземпляра, которое разрешает чтение, создается ошибка, и дальнейшие действия не выполняются. - Поиск элементов выполняется
E
с идентификаторомMoveNext
и без аргументов типа. Если подстановка элемента не соответствует, результатом является ошибка или результатом является что-либо, кроме группы методов, создается ошибка, и никакие дальнейшие действия не выполняются. - Разрешение перегрузки выполняется в группе методов с пустым списком аргументов. Если разрешение перегрузки не приводит к применению применимых методов, приводит к неоднозначности или приводит к одному лучшему методу, но этот метод является статическим или не общедоступным, или его тип возвращаемого значения отсутствует
bool
, возникает ошибка и дальнейшие действия не выполняются. - Тип коллекции —
X
это типE
перечислителя, а тип итерации — это типCurrent
свойства. СвойствоCurrent
может включатьref
модификатор, в этом случае возвращаемое выражение является variable_reference (§9.5), которое при необходимости доступно только для чтения.
- Выполните поиск элементов для типа
- В противном случае проверьте наличие перечисленного интерфейса:
- Если среди всех типов
Tᵢ
, из которых выполняется неявное преобразованиеX
IEnumerable<Tᵢ>
, существует уникальный тип, который неT
существует, и для всех остальныхT
существует неявное преобразование изdynamic
Tᵢ
, то типIEnumerable<T>
коллекции — интерфейсIEnumerable<Tᵢ>
, тип перечислителя — интерфейс, а тип итерации — этоIEnumerable<T>
интерфейсIEnumerator<T>
.T
- В противном случае, если существует несколько таких типов
T
, возникает ошибка и дальнейшие действия не выполняются. - В противном случае, если есть неявное преобразование из
X
System.Collections.IEnumerable
интерфейса, то тип коллекции — это интерфейс, тип перечислителя — интерфейсSystem.Collections.IEnumerator
, а тип итерации — этоobject
. - В противном случае возникает ошибка, и дальнейшие действия не выполняются.
- Если среди всех типов
Приведенные выше шаги, если успешно, однозначно создают тип коллекции, тип C
перечислителя и тип E
T
итерации, ref T
или ref readonly T
. Оператор foreach
формы
foreach (V v in x) «embedded_statement»
Затем эквивалентен следующему:
{
E e = ((C)(x)).GetEnumerator();
try
{
while (e.MoveNext())
{
V v = (V)(T)e.Current;
«embedded_statement»
}
}
finally
{
... // Dispose e
}
}
Переменная e
не видна или недоступна для выражения x
или внедренной инструкции или любого другого исходного кода программы. Переменная v
доступна только для чтения в внедренной инструкции. Если не существует явного преобразования (§10.3T
типа итерации) V
в (local_variable_type в foreach
инструкции), создается ошибка и дальнейшие действия не выполняются.
Если переменная итерации является эталонной переменной (§9.7), foreach
оператор формы
foreach (ref V v in x) «embedded_statement»
Затем эквивалентен следующему:
{
E e = ((C)(x)).GetEnumerator();
try
{
while (e.MoveNext())
{
ref V v = ref e.Current;
«embedded_statement»
}
}
finally
{
... // Dispose e
}
}
Переменная e
не видна или недоступна для выражения x
или внедренной инструкции или любого другого исходного кода программы. Эталонная переменная v
является чтением и записью в внедренной инструкции, но v
не должна быть переназначаемой (§12.21.3). Если преобразование удостоверений (§10.2.2V
local_variable_type в foreach
инструкции), возникает ошибка, и дальнейшие действия не выполняются.
Оператор foreach
формы foreach (ref readonly V v in x) «embedded_statement»
имеет аналогичную эквивалентную форму, но ссылочная переменная v
находится ref readonly
в внедренной инструкции, поэтому не может быть переназначен или переназначен.
Примечание. Если
x
имеется значениеnull
,System.NullReferenceException
создается исключение во время выполнения. конечная заметка
Реализация разрешена реализовать заданный foreach_statement по-разному, например, по соображениям производительности, если поведение соответствует приведенному выше расширению.
Размещение v
внутри while
цикла важно для отслеживания (§12.19.6.2) любой анонимной функцией, возникающей в embedded_statement.
Пример:
int[] values = { 7, 9, 13 }; Action f = null; foreach (var value in values) { if (f == null) { f = () => Console.WriteLine("First value: " + value); } } f();
Если
v
в развернутой форме объявлены внеwhile
цикла, оно будет совместно использоваться всеми итерациями, а его значение послеfor
цикла будет окончательным значением,13
что является вызовомf
печати. Вместо этого, так как каждая итерация имеет свою собственную переменнуюv
, которая будет записанаf
в первой итерации, будет продолжать содержать значение7
, которое будет напечатано. (Обратите внимание, что более ранние версии C# объявленыv
внеwhile
цикла.)пример конца
Тело finally
блока построено на следующих шагах:
Если есть неявное преобразование из
E
System.IDisposable
интерфейса, тоЕсли
E
тип значения, не допускающий значение NULL,finally
предложение расширяется до семантического эквивалента:finally { ((System.IDisposable)e).Dispose(); }
finally
В противном случае предложение развертывается до семантического эквивалента:finally { System.IDisposable d = e as System.IDisposable; if (d != null) { d.Dispose(); } }
за исключением того, что если
E
это тип значения или параметр типа, созданный в тип значения, преобразованиеe
System.IDisposable
не должно привести к возникновению бокса.
В противном случае, если
E
тип запечатан,finally
предложение расширяется до пустого блока:finally {}
finally
В противном случае предложение развернуто в следующих случаях:finally { System.IDisposable d = e as System.IDisposable; if (d != null) { d.Dispose(); } }
Локальная переменная d
не видна или недоступна для любого пользовательского кода. В частности, он не конфликтует с любой другой переменной, область которой включает finally
блок.
Порядок, в котором foreach
проходит элементы массива, выглядит следующим образом: для элементов одномерных массивов проходят по возрастанию порядка индексов, начиная с индекса 0 и заканчивая индексом Length – 1
. Для многомерных массивов элементы пересекаются таким образом, чтобы индексы самого правого измерения были увеличены сначала, а затем следующее левое измерение и т. д. слева.
Пример. Следующий пример выводит каждое значение в двухмерном массиве в порядке элемента:
class Test { static void Main() { double[,] values = { {1.2, 2.3, 3.4, 4.5}, {5.6, 6.7, 7.8, 8.9} }; foreach (double elementValue in values) { Console.Write($"{elementValue} "); } Console.WriteLine(); } }
Выходные данные создаются следующим образом:
1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9
пример конца
Пример. В следующем примере
int[] numbers = { 1, 3, 5, 7, 9 }; foreach (var n in numbers) { Console.WriteLine(n); }
Тип выводимого
n
int
значения — типnumbers
итерации .пример конца
Операторы перехода 13.10
13.10.1 Общие
Операторы перехода безусловно передают управление.
jump_statement
: break_statement
| continue_statement
| goto_statement
| return_statement
| throw_statement
;
Расположение, в которое передается оператор перехода, называется целевым объектом инструкции перехода.
Когда заявление о переходе происходит в блоке, и цель этого заявления прыжка находится за пределами этого блока, говорится, что выйдите из блока. Хотя оператор перехода может передавать элемент управления из блока, он никогда не может передавать управление в блок.
Выполнение инструкций перехода сложно благодаря присутствию промежуточных try
инструкций. В отсутствие таких try
заявлений оператор прыжка безоговорочно передает контроль от оператора перехода к целевому объекту. В присутствии таких промежуточных try
инструкций выполнение является более сложным. Если оператор перехода выходит из одного или нескольких try
блоков с связанными finally
блоками, элемент управления изначально передается finally
в блок самой try
внутренней инструкции. Когда и если элемент управления достигает конечной finally
точки блока, элемент управления передается finally
в блок следующей заключающей инструкции try
. Этот процесс повторяется до тех пор, пока finally
блоки всех промежуточных try
инструкций не будут выполнены.
Пример. В следующем коде
class Test { static void Main() { while (true) { try { try { Console.WriteLine("Before break"); break; } finally { Console.WriteLine("Innermost finally block"); } } finally { Console.WriteLine("Outermost finally block"); } } Console.WriteLine("After break"); } }
finally
Блоки, связанные с двумяtry
операторами, выполняются перед передачей элемента управления в целевой объект инструкции jump. Выходные данные создаются следующим образом:Before break Innermost finally block Outermost finally block After break
пример конца
13.10.2 Оператор перерыва
Оператор break
выходит из ближайшего включающего switch
оператора , while
, do
или for
foreach
оператора.
break_statement
: 'break' ';'
;
Цель инструкции break
— это конечная точка ближайшего заключенногоswitch
, while
или do
for
foreach
оператора.
break
Если инструкция не заключена в switch
оператор , while
do
for
или foreach
оператор, возникает ошибка во время компиляции.
Если несколькоswitch
while
, , do
или for
foreach
операторы вложены друг в друга, break
оператор применяется только к самой внутренней инструкции. Для передачи управления на нескольких уровнях goto
вложения необходимо использовать оператор (§13.10.4).
Оператор break
не может выйти из finally
блока (§13.11).
break
При возникновении инструкции в finally
блоке целевой объект инструкции break
должен находиться в одном finally
блоке; в противном случае возникает ошибка во время компиляции.
Оператор break
выполняется следующим образом:
-
break
Если оператор выходит из одного или несколькихtry
блоков с связаннымиfinally
блоками, элемент управления изначально передаетсяfinally
в блок самойtry
внутренней инструкции. Когда и если элемент управления достигает конечнойfinally
точки блока, элемент управления передаетсяfinally
в блок следующей заключающей инструкцииtry
. Этот процесс повторяется до тех пор, покаfinally
блоки всех промежуточныхtry
инструкций не будут выполнены. - Элемент управления передается в целевой объект инструкции
break
.
break
Поскольку оператор безоговорочно передает контроль в другом месте, конечный пункт break
оператора никогда недоступен.
13.10.3 Инструкция continue
Оператор continue
запускает новую итерацию ближайшего заключенногоwhile
, do
for
или foreach
оператора.
continue_statement
: 'continue' ';'
;
Цель инструкции continue
— это конечная точка внедренного оператора ближайшего заключенногоwhile
, do
for
или foreach
оператора.
continue
Если инструкция не заключена в while
оператор , do
for
или foreach
оператор, возникает ошибка во время компиляции.
Если несколько while
do
операторов или for
foreach
операторов вложены друг в друга, continue
оператор применяется только к самой внутренней инструкции. Для передачи управления на нескольких уровнях goto
вложения необходимо использовать оператор (§13.10.4).
Оператор continue
не может выйти из finally
блока (§13.11).
continue
При возникновении инструкции в finally
блоке целевой объект инструкции continue
должен находиться в одном finally
блоке; в противном случае возникает ошибка во время компиляции.
Оператор continue
выполняется следующим образом:
-
continue
Если оператор выходит из одного или несколькихtry
блоков с связаннымиfinally
блоками, элемент управления изначально передаетсяfinally
в блок самойtry
внутренней инструкции. Когда и если элемент управления достигает конечнойfinally
точки блока, элемент управления передаетсяfinally
в блок следующей заключающей инструкцииtry
. Этот процесс повторяется до тех пор, покаfinally
блоки всех промежуточныхtry
инструкций не будут выполнены. - Элемент управления передается в целевой объект инструкции
continue
.
continue
Поскольку оператор безоговорочно передает контроль в другом месте, конечный пункт continue
оператора никогда недоступен.
13.10.4 Инструкция goto
Оператор goto
передает элемент управления инструкции, помеченной меткой.
goto_statement
: 'goto' identifier ';'
| 'goto' 'case' constant_expression ';'
| 'goto' 'default' ';'
;
Цель инструкции идентификатора goto
— это помеченная инструкция с заданной меткой. Если метка с заданным именем не существует в текущем члене функции или если goto
инструкция не находится в области метки, возникает ошибка во время компиляции.
Примечание. Это правило позволяет использовать инструкцию для передачи
goto
управления из вложенной области, но не в вложенную область. В примереclass Test { static void Main(string[] args) { string[,] table = { {"Red", "Blue", "Green"}, {"Monday", "Wednesday", "Friday"} }; foreach (string str in args) { int row, colm; for (row = 0; row <= 1; ++row) { for (colm = 0; colm <= 2; ++colm) { if (str == table[row,colm]) { goto done; } } } Console.WriteLine($"{str} not found"); continue; done: Console.WriteLine($"Found {str} at [{row}][{colm}]"); } } }
goto
Оператор используется для передачи элемента управления из вложенной области.конечная заметка
Цель инструкции goto case
— это список инструкций в немедленном заключенном switch
операторе (§13.8.3), который содержит case
метку с константным шаблоном заданного значения константы и без защиты.
goto case
Если инструкция не заключена switch
в оператор, если ближайшее switch
заключающее оператор не содержит такого case
значения или если constant_expression неявно преобразуется (§10.2) в руководящий тип ближайшего switch
заключающего оператора, возникает ошибка во время компиляции.
Цель инструкции — это список инструкций goto default
в немедленном заключенном switch
операторе (default
), который содержит метку.
goto default
Если инструкция не заключена switch
в инструкцию или если ближайшая switch
заключиющая инструкция не содержит default
метку, возникает ошибка во время компиляции.
Оператор goto
не может выйти из finally
блока (§13.11).
goto
При возникновении инструкции finally
в goto
блоке целевой объект инструкции должен находиться в одном finally
блоке или в противном случае возникает ошибка во время компиляции.
Оператор goto
выполняется следующим образом:
-
goto
Если оператор выходит из одного или несколькихtry
блоков с связаннымиfinally
блоками, элемент управления изначально передаетсяfinally
в блок самойtry
внутренней инструкции. Когда и если элемент управления достигает конечнойfinally
точки блока, элемент управления передаетсяfinally
в блок следующей заключающей инструкцииtry
. Этот процесс повторяется до тех пор, покаfinally
блоки всех промежуточныхtry
инструкций не будут выполнены. - Элемент управления передается в целевой объект инструкции
goto
.
goto
Поскольку оператор безоговорочно передает контроль в другом месте, конечный пункт goto
оператора никогда недоступен.
13.10.5 Инструкция return
Оператор return
возвращает элемент управления текущим вызывающим элементом функции, в котором отображается оператор return, при необходимости возвращая значение или variable_reference (§9.5).
return_statement
: 'return' ';'
| 'return' expression ';'
| 'return' 'ref' variable_reference ';'
;
Return_statement без выражения называется возвращаемым значением; один из содержащих выражение называется return-by-ref; и один, содержащий только ref
, называется возвращаемым по значению.
Это ошибка во время компиляции для использования возвращаемого значения из метода, объявленного как возвращаемое по значению или возвращаемое по значению (§15.6.1).
Это ошибка во время компиляции для использования возвращаемого по ссылке метода, объявленного как возвращаемое значение без значения или возвращаемое по значению.
Это ошибка времени компиляции для использования возвращаемого по значению из метода, объявленного как возвращаемое значение без значения или возвращаемое по ссылке.
Это ошибка времени компиляции для использования возвращаемого по ссылке, если выражение не является variable_reference или является ссылкой на переменную, ref-safe-context не является вызывающим контекстом (§9.7.2).
Это ошибка времени компиляции для использования возвращаемого по ссылке метода, объявленного с помощью method_modifierasync
.
Как сообщается, член функции вычисляет значение , если это метод с методом возвращаемого по значению (§15.6.11), методом доступа по значению свойства или индексатора или определяемым пользователем оператором. Члены функции, возвращающие значение, не вычисляют значение и являются методами с эффективным типом возвращаемого значения void
, задают методы доступа свойств и индексаторов, добавляют и удаляют методы доступа событий, конструкторы экземпляров, статические конструкторы и методы завершения. Члены функции, возвращаемые по ссылке, не вычисляют значение.
Для возвращаемого значения неявное преобразование (§10.2) должно существовать от типа выражения к эффективному типу возвращаемого значения (§15.6.11) содержащего элемента функции. Для возвращаемого по ссылке преобразование удостоверений (§10.2.2) должно существовать между типом выражения и эффективным типом возвращаемого элемента функции.
return
операторы также можно использовать в тексте анонимных выражений функций (§12.19) и участвовать в определении того, какие преобразования существуют для этих функций (§10.7.1).
Это ошибка во время компиляции для оператора, return
отображаемого finally
в блоке (§13.11).
Оператор return
выполняется следующим образом:
- Для возвращаемого по значению выражение вычисляется и его значение преобразуется в эффективный тип возвращаемой функции путем неявного преобразования. Результат преобразования становится результатом, созданным функцией. Для возвращаемого по ссылке выражение вычисляется, а результат должен классифицироваться как переменная. Если возвращается по ссылке
readonly
метода, результирующая переменная доступна только для чтения. -
return
Если инструкция заключена в один или несколькоtry
catch
блоков с связаннымиfinally
блоками, элемент управления изначально передаетсяfinally
блоку самойtry
внутренней инструкции. Когда и если элемент управления достигает конечнойfinally
точки блока, элемент управления передаетсяfinally
в блок следующей заключающей инструкцииtry
. Этот процесс повторяется до тех пор,finally
пока блоки всех вложенныхtry
инструкций не будут выполнены. - Если содержащая функция не является асинхронной, элемент управления возвращается вызывающей функции, содержащей функцию вместе со значением результата, если таковой имеется.
- Если содержащая функция является асинхронной, элемент управления возвращается текущему вызывающому объекту, а результирующий значение, если таковой имеется, записывается в задаче возврата, как описано в статье 15.15.3.
return
Поскольку оператор безоговорочно передает контроль в другом месте, конечный пункт return
оператора никогда недоступен.
13.10.6 Оператор throw
Оператор throw
создает исключение.
throw_statement
: 'throw' expression? ';'
;
Оператор throw
с выражением создает исключение, созданное при оценке выражения. Выражение должно быть неявно System.Exception
преобразовано в, и результат оценки выражения преобразуется в System.Exception
значение перед созданием. Если результат преобразования является null
результатом преобразования, System.NullReferenceException
создается вместо него.
Оператор throw
без выражения нельзя использовать только в catch
блоке, в этом случае этот оператор повторно создает исключение, которое в настоящее время обрабатывается этим catch
блоком.
throw
Поскольку оператор безоговорочно передает контроль в другом месте, конечный пункт throw
оператора никогда недоступен.
При возникновении исключения элемент управления передается первому catch
предложению в заключающей try
инструкции, которая может обрабатывать исключение. Процесс, который происходит с точки исключения, вызываемого до точки передачи элемента управления в подходящий обработчик исключений, называется распространением исключений. Распространение исключения состоит из многократного вычисления следующих шагов до тех пор, пока catch
не будет найдено предложение, соответствующее исключению. В этом описании точка броска изначально является расположением, в котором создается исключение. Это поведение указано в (§21.4).
В текущем элементе функции проверяется каждая
try
инструкция, заключающая точку броска. Для каждой инструкции, начиная с самой внутреннейS
инструкцииtry
и заканчивая самой внешнейtry
инструкцией, вычисляются следующие действия:-
try
Если блокS
заключает точку броска иS
имеет одно или несколькоcatch
предложений,catch
предложения проверяются в порядке внешнего вида, чтобы найти подходящий обработчик исключения. Первоеcatch
предложение, указывающее типT
исключения (или параметр типа, обозначающий типT
исключения во время выполнения), таким образом, что типE
времени выполнения наследуется отT
соответствия. Если предложение содержит фильтр исключений, объект исключения назначается переменной исключения, а фильтр исключений вычисляется.catch
Если предложение содержит фильтр исключений, этоcatch
предложение считается совпадением, если фильтр исключений оцениваетсяtrue
. Общееcatch
предложение (§13.11) считается совпадением для любого типа исключения.catch
Если находится соответствующее предложение, распространение исключений завершается путем передачи элемента управления в блок этогоcatch
предложения. - В противном случае, если
try
блок илиcatch
блокS
заключает точку броска иS
если имеетfinally
блок, элемент управления передается вfinally
блок. Если блокfinally
создает другое исключение, обработка текущего исключения завершается. В противном случае, когда элемент управления достигает конечной точки блока, обработка текущегоfinally
исключения продолжается.
-
Если обработчик исключений не был расположен в текущем вызове функции, вызов функции завершается, и происходит одно из следующих действий:
Если текущая функция не является асинхронной, описанные выше шаги повторяются для вызывающего функции с точкой вызова, соответствующей инструкции, из которой был вызван член функции.
Если текущая функция является асинхронной и возвращаемой задачей, исключение записывается в задаче возврата, которая помещается в состояние сбоя или отмены, как описано в разделе 15.15.3.
Если текущая функция является асинхронной и
void
возвращается, контекст синхронизации текущего потока уведомляется, как описано в разделе 15.15.4.
Если обработка исключений завершает вызов всех элементов функции в текущем потоке, указывая, что поток не имеет обработчика исключения, то сам поток завершается. Влияние такого прекращения определяется реализацией.
13.11 Инструкция try
Оператор try
предоставляет механизм перехвата исключений, возникающих во время выполнения блока. Кроме того, try
инструкция предоставляет возможность указывать блок кода, который всегда выполняется при выходе try
элемента управления из оператора.
try_statement
: 'try' block catch_clauses
| 'try' block catch_clauses? finally_clause
;
catch_clauses
: specific_catch_clause+
| specific_catch_clause* general_catch_clause
;
specific_catch_clause
: 'catch' exception_specifier exception_filter? block
| 'catch' exception_filter block
;
exception_specifier
: '(' type identifier? ')'
;
exception_filter
: 'when' '(' boolean_expression ')'
;
general_catch_clause
: 'catch' block
;
finally_clause
: 'finally' block
;
Try_statement состоит из ключевого слова, за которым следует try
, а затем ноль или более catch_clauses, а затем необязательный finally_clause. Должно быть не менее одного catch_clause или finally_clause.
В exception_specifier тип или его действующий базовый класс, если он является type_parameter, должен быть System.Exception
или тип, производный от него.
catch
Если предложение задает как class_type, так и идентификатор, объявляется переменная исключения заданного имени и типа. Переменная исключения вводится в пространство объявления specific_catch_clause (§7.3). Во время выполнения exception_filter и catch
блока переменная исключения представляет исключение, которое в настоящее время обрабатывается. В целях проверки определенного назначения переменная исключения считается определенно назначенной в всей области.
Если catch
предложение не содержит имя переменной исключения, доступ к объекту исключения в фильтре и catch
блоке невозможно.
Предложение catch
, указывающее ни тип исключения, ни имя переменной исключения, называется общим catch
предложением. Оператор try
может иметь только одно общее catch
предложение, и, если он присутствует, это должно быть последнее catch
предложение.
Примечание. Некоторые языки программирования могут поддерживать исключения, которые не представляются как объект, производный от
System.Exception
, хотя такие исключения никогда не могут быть созданы кодом C#. Общееcatch
предложение может использоваться для перехвата таких исключений. Таким образом, общееcatch
предложение семантически отличается от одного, указывающего типSystem.Exception
, в том, что бывший может также перехватывать исключения из других языков. конечная заметка
Чтобы найти обработчик исключения, catch
предложения рассматриваются в лексическом порядке.
catch
Если предложение указывает тип, но не фильтр исключений, это ошибка во время компиляции для более поздней catch
инструкцииtry
, чтобы указать тип, который совпадает с типом или производным от этого типа.
Примечание. Без этого ограничения можно было бы написать недоступные
catch
предложения. конечная заметка
В блоке catch
throw
оператор (§13.10.6) без выражения нельзя использовать для повторного создания исключения, поймаемого catch
блоком. Назначения переменной исключений не изменяют исключение, которое создается повторно.
Пример. В следующем коде
class Test { static void F() { try { G(); } catch (Exception e) { Console.WriteLine("Exception in F: " + e.Message); e = new Exception("F"); throw; // re-throw } } static void G() => throw new Exception("G"); static void Main() { try { F(); } catch (Exception e) { Console.WriteLine("Exception in Main: " + e.Message); } } }
Метод
F
перехватывает исключение, записывает некоторые диагностические сведения в консоль, изменяет переменную исключения и повторно создает исключение. Исключение, которое создается повторно, является исходным исключением, поэтому выходные данные создаются:Exception in F: G Exception in Main: G
Если первый
catch
блок был созданe
вместо повторного увеличения текущего исключения, выходные данные будут вычислиться следующим образом:Exception in F: G Exception in Main: F
пример конца
Это ошибка во время компиляции для оператора или break
оператора для continue
goto
передачи элемента управления из finally
блока.
break
continue
При возникновении в блоке goto
инструкции или finally
инструкции целевой объект инструкции должен находиться в одном finally
блоке или в противном случае возникает ошибка во время компиляции.
Это ошибка во время компиляции для инструкции, return
возникающей в блоке finally
.
Когда выполнение достигает инструкции try
, элемент управления передается в try
блок. Если элемент управления достигает конечной try
точки блока без исключения, элемент управления передается finally
в блок, если он существует. Если блок не finally
существует, элемент управления передается в конечную точку инструкции try
.
Если исключение было распространено, предложения, если таковые имеются, catch
рассматриваются в лексическом порядке искать первое совпадение для исключения. Поиск предложения сопоставления catch
продолжается со всеми вложенными блоками, как описано в разделе 13.10.6. Предложение catch
— это совпадение, если тип исключения соответствует любому exception_specifier , а любой exception_filter имеет значение true. Предложение catch
без exception_specifier соответствует любому типу исключения. Тип исключения соответствует exception_specifier, если exception_specifier указывает тип исключения или базовый тип типа исключения. Если предложение содержит фильтр исключений, объект исключения назначается переменной исключения, а фильтр исключений вычисляется.
Если исключение было распространено и catch
найдено соответствующее предложение, элемент управления передается в первый блок сопоставления catch
. Если элемент управления достигает конечной catch
точки блока без исключения, элемент управления передается finally
в блок, если он существует. Если блок не finally
существует, элемент управления передается в конечную точку инструкции try
. Если исключение было распространено из catch
блока, элемент управления передается finally
в блок, если он существует. Исключение распространяется на следующую заключиющую инструкцию try
.
Если исключение было распространено, и предложение сопоставления catch
не найдено, контроль передается в finally
блок, если он существует. Исключение распространяется на следующую заключиющую инструкцию try
.
Операторы блока finally
выполняются всегда, когда оператор try
теряет управление. Это верно, происходит ли передача элемента управления в результате нормального выполнения, в результате выполнения break
инструкции , continue
goto
или return
инструкции или в результате распространения исключения из инструкцииtry
. Если элемент управления достигает конечной finally
точки блока без исключения, элемент управления передается в конечную точку инструкции try
.
Если исключение возникает во время выполнения finally
блока и не перехватывается в finally
одном блоке, исключение распространяется на следующую заключающую try
инструкцию. Если другое исключение было в процессе распространения, это исключение будет потеряно. Процесс распространения исключения рассматривается далее в описании инструкции throw
(§13.10.6).
Пример. В следующем коде
public class Test { static void Main() { try { Method(); } catch (Exception ex) when (ExceptionFilter(ex)) { Console.WriteLine("Catch"); } bool ExceptionFilter(Exception ex) { Console.WriteLine("Filter"); return true; } } static void Method() { try { throw new ArgumentException(); } finally { Console.WriteLine("Finally"); } } }
Метод
Method
вызывает исключение. Первое действие — проверить вложенныеcatch
предложения, выполняя все фильтры исключений.finally
Затем предложение выполняетсяMethod
перед передачей элемента управления в заключенноеcatch
соответствующее предложение. Результирующий результат:Filter Finally Catch
пример конца
Блок try
инструкции try
доступен, если оператор try
доступен.
Блок catch
инструкции try
доступен, если оператор try
доступен.
Блок finally
инструкции try
доступен, если оператор try
доступен.
Конечная точка try
оператора может быть достигнута, если оба из следующих значений имеют значение true:
- Конечная точка
try
блока может быть достигнута или конечная точка по крайней мере одногоcatch
блока может быть достигнута. - Если блок присутствует, конечная
finally
точкаfinally
блока становится доступной.
13.12 Проверенные и снятые инструкции
Инструкции checked
используются для управления контекстом unchecked
проверки переполнения для арифметических операций и преобразований целочисленного типа.
checked_statement
: 'checked' block
;
unchecked_statement
: 'unchecked' block
;
Оператор checked
приводит к оценке всех выражений в блоке в проверяемом контексте, а unchecked
инструкция приводит к оценке всех выражений в блоке в неспроверяемом контексте.
Операторы checked
и unchecked
checked
операторы точно эквивалентны unchecked
операторам (§12.8.20), за исключением того, что они работают над блоками вместо выражений.
13.13 Инструкция блокировки
Оператор lock
получает блокировку взаимного исключения для заданного объекта, выполняет инструкцию, а затем освобождает блокировку.
lock_statement
: 'lock' '(' expression ')' embedded_statement
;
Выражение lock
должно указывать значение типа, известного как ссылка. Неявное преобразование бокса (§10.2.9) никогда не выполняется для выраженияlock
инструкции, поэтому это ошибка во время компиляции для выражения, указывающего значение value_type.
Оператор lock
формы
lock (x)
…
где x
является выражением reference_type, точно эквивалентно следующим:
bool __lockWasTaken = false;
try
{
System.Threading.Monitor.Enter(x, ref __lockWasTaken);
...
}
finally
{
if (__lockWasTaken)
{
System.Threading.Monitor.Exit(x);
}
}
за исключением того, что x
вычисляется только один раз.
Хотя блокировка взаимного исключения сохраняется, код, выполняемый в одном потоке выполнения, также может получить и освободить блокировку. Однако код, выполняемый в других потоках, блокируется получение блокировки, пока блокировка не будет освобождена.
13.14 Инструкция using
Оператор using
получает один или несколько ресурсов, выполняет инструкцию, а затем удаляет ресурс.
using_statement
: 'using' '(' resource_acquisition ')' embedded_statement
;
resource_acquisition
: local_variable_declaration
| expression
;
Ресурс — это класс или структура, реализующая System.IDisposable
интерфейс, который включает в себя один метод Dispose
без параметров. Код, использующий ресурс, может Dispose
вызвать, чтобы указать, что ресурс больше не нужен.
Если форма resource_acquisition local_variable_declaration, то тип local_variable_declaration должен быть либо dynamic
типом, в который можно неявно System.IDisposable
преобразовать. Если форма выражения resource_acquisition является выражением , это выражение должно быть неявно преобразовано в System.IDisposable
.
Локальные переменные, объявленные в resource_acquisition , доступны только для чтения, и должны включать инициализатор. Ошибка во время компиляции возникает, если внедренная инструкция пытается изменить эти локальные переменные (с помощью назначения или ++
--
операторов), принять адрес или передать их в качестве ссылочных или выходных параметров.
Оператор using
преобразуется в три части: приобретение, использование и удаление. Использование ресурса неявно заключено в try
инструкцию, включающую finally
предложение. Это finally
предложение удаляет ресурс.
null
Если ресурс получен, вызов Dispose
не выполняется, и исключение не возникает. Если ресурс имеет тип dynamic
, он динамически преобразуется с помощью неявного динамического преобразования (§10.2.10) IDisposable
во время приобретения, чтобы убедиться, что преобразование успешно выполнено до использования и удаления.
Оператор using
формы
using (ResourceType resource = «expression» ) «statement»
соответствует одному из трех возможных расширений. Если ResourceType
имеет тип значения, не допускающий значение NULL, или параметр типа с ограничением типа значения (§15.2.5), расширение семантически эквивалентно
{
ResourceType resource = «expression»;
try
{
«statement»;
}
finally
{
((IDisposable)resource).Dispose();
}
}
за исключением того, что приведение resource
System.IDisposable
не должно привести к возникновению бокса.
В противном случае, когда ResourceType
это dynamic
расширение
{
ResourceType resource = «expression»;
IDisposable d = resource;
try
{
«statement»;
}
finally
{
if (d != null)
{
d.Dispose();
}
}
}
В противном случае расширение равно
{
ResourceType resource = «expression»;
try
{
«statement»;
}
finally
{
IDisposable d = (IDisposable)resource;
if (d != null)
{
d.Dispose();
}
}
}
В любом расширении resource
переменная доступна только для чтения в внедренной инструкции, и d
переменная недоступна и невидима для внедренной инструкции.
Реализация разрешена реализовать заданный using_statement по-разному, например по соображениям производительности, если поведение соответствует приведенному выше расширению.
Оператор using
формы:
using («expression») «statement»
имеет те же три возможных расширения. В этом случае ResourceType
неявно тип времени компиляции выражения, если он имеет один. В противном случае сам интерфейс IDisposable
используется в качестве ResourceType
. Переменная resource
недоступна и невидима для внедренной инструкции.
Если resource_acquisition принимает форму local_variable_declaration, можно получить несколько ресурсов заданного типа. Оператор using
формы
using (ResourceType r1 = e1, r2 = e2, ..., rN = eN) «statement»
точно эквивалентен последовательности вложенных using
операторов:
using (ResourceType r1 = e1)
using (ResourceType r2 = e2)
...
using (ResourceType rN = eN)
«statement»
Пример. В приведенном ниже примере создается файл с именем log.txt и записывается две строки текста в файл. Затем в этом примере открывается тот же файл для чтения и копирования содержащихся строк текста в консоль.
class Test { static void Main() { using (TextWriter w = File.CreateText("log.txt")) { w.WriteLine("This is line one"); w.WriteLine("This is line two"); } using (TextReader r = File.OpenText("log.txt")) { string s; while ((s = r.ReadLine()) != null) { Console.WriteLine(s); } } } }
TextWriter
TextReader
Так как классы реализуютIDisposable
интерфейс, пример может использоватьusing
инструкции, чтобы убедиться, что базовый файл правильно закрыт после операций записи или чтения.пример конца
13.15 Оператор доходности
Оператор yield
используется в блоке итератора (§13.3), чтобы получить значение объекту перечислителя (§15.14.5) или перечислению объекта (§15.14.6) итератора или сигнализировать о завершении итерации.
yield_statement
: 'yield' 'return' expression ';'
| 'yield' 'break' ';'
;
yield
— это контекстное ключевое слово (§6.4.4) и имеет особое значение только при использовании непосредственно перед или return
ключевым словомbreak
.
Существует несколько ограничений, в которых yield
может отображаться инструкция, как описано в следующем разделе.
- Это ошибка времени компиляции для
yield
инструкции (любой формы), отображаемой вне method_body, operator_body или accessor_body. - Это ошибка во время компиляции для
yield
инструкции (любой формы), отображаемой внутри анонимной функции. - Это ошибка времени компиляции для
yield
инструкции (любой формы), отображаемой вfinally
предложении инструкцииtry
. - Это ошибка во время компиляции для
yield return
оператора, отображаемого вtry
любом месте инструкции, содержащей любые catch_clauses.
Пример. В следующем примере показаны некоторые допустимые и недопустимые использование инструкций
yield
.delegate IEnumerable<int> D(); IEnumerator<int> GetEnumerator() { try { yield return 1; // Ok yield break; // Ok } finally { yield return 2; // Error, yield in finally yield break; // Error, yield in finally } try { yield return 3; // Error, yield return in try/catch yield break; // Ok } catch { yield return 4; // Error, yield return in try/catch yield break; // Ok } D d = delegate { yield return 5; // Error, yield in an anonymous function }; } int MyMethod() { yield return 1; // Error, wrong return type for an iterator block }
пример конца
Неявное преобразование (§10.2yield return
§15.14.4) итератора.
Оператор yield return
выполняется следующим образом:
- Выражение, заданное в инструкции, вычисляется, неявно преобразуется в тип доходности и назначается
Current
свойству объекта перечислителя. - Выполнение блока итератора приостановлено.
yield return
Если оператор находится в одном или несколькихtry
блоках, связанныеfinally
блоки не выполняются в настоящее время. - Метод
MoveNext
объекта перечислителя возвращается вызывающей объекту, указывая, что объект перечислителя успешно продвинулсяtrue
к следующему элементу.
Следующий вызов метода перечислителя MoveNext
возобновляет выполнение блока итератора из места последнего приостановки.
Оператор yield break
выполняется следующим образом:
-
yield break
Если оператор заключен в один или несколькоtry
блоков со связаннымиfinally
блоками, элемент управления изначально передаетсяfinally
в блок самойtry
внутренней инструкции. Когда и если элемент управления достигает конечнойfinally
точки блока, элемент управления передаетсяfinally
в блок следующей заключающей инструкцииtry
. Этот процесс повторяется до тех пор,finally
пока блоки всех вложенныхtry
инструкций не будут выполнены. - Элемент управления возвращается вызывающему блоку итератора. Это
MoveNext
метод илиDispose
метод объекта перечислителя.
yield break
Поскольку оператор безоговорочно передает контроль в другом месте, конечный пункт yield break
оператора никогда недоступен.
ECMA C# draft specification