13 Instrucciones
13.1 General
C# proporciona una variedad de instrucciones.
Nota: La mayoría de estas instrucciones serán familiares para los desarrolladores que han programado en C y C++. nota final
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) y fixed_statement (§23.7) solo están disponibles en código no seguro (§23).
El embedded_statement noterminal se usa para instrucciones que aparecen dentro de otras instrucciones. El uso de embedded_statement en lugar de instrucción excluye el uso de instrucciones de declaración y instrucciones etiquetadas en estos contextos.
Ejemplo: el código
void F(bool b) { if (b) int i = 44; }
produce un error en tiempo de compilación porque una
if
instrucción requiere un embedded_statement en lugar de una instrucción para suif
rama. Si se permitía este código, la variablei
se declararía, pero nunca se podría usar. Tenga en cuenta, sin embargo, que al colocari
la declaración de en un bloque, el ejemplo es válido.ejemplo final
13.2 Puntos finales y accesibilidad
Cada instrucción tiene un punto de conexión. En términos intuitivos, el punto final de una instrucción es la ubicación que sigue inmediatamente a la instrucción . Las reglas de ejecución para instrucciones compuestas (instrucciones que contienen instrucciones incrustadas) especifican la acción que se realiza cuando el control alcanza el punto final de una instrucción incrustada.
Ejemplo: cuando el control alcanza el punto final de una instrucción en un bloque, el control se transfiere a la siguiente instrucción del bloque. ejemplo final
Si es posible que se pueda acceder a una instrucción mediante la ejecución, se dice que la instrucción es accesible. Por el contrario, si no existe la posibilidad de que se ejecute una instrucción, se dice que la instrucción es inaccesible.
Ejemplo: en el código siguiente
void F() { Console.WriteLine("reachable"); goto Label; Console.WriteLine("unreachable"); Label: Console.WriteLine("reachable"); }
la segunda invocación de Console.WriteLine no es accesible porque no existe la posibilidad de que se ejecute la instrucción .
ejemplo final
Se notifica una advertencia si no se puede acceder a una instrucción distinta de throw_statement, bloque o empty_statement . En concreto, no es un error para que una instrucción sea inaccesible.
Nota: Para determinar si se puede acceder a una instrucción determinada o un punto de conexión, el compilador realiza el análisis de flujo según las reglas de accesibilidad definidas para cada instrucción. El análisis de flujo tiene en cuenta los valores de las expresiones constantes (§12.23) que controlan el comportamiento de las instrucciones, pero no se tienen en cuenta los valores posibles de expresiones no constantes. En otras palabras, con fines de análisis de flujo de control, se considera que una expresión no constante de un tipo determinado tiene cualquier valor posible de ese tipo.
En el ejemplo
void F() { const int i = 1; if (i == 2) Console.WriteLine("unreachable"); }
La expresión booleana de la
if
instrucción es una expresión constante porque ambos operandos del==
operador son constantes. A medida que la expresión constante se evalúa en tiempo de compilación, la generación del valorfalse
, laConsole.WriteLine
invocación se considera inaccesible. Sin embargo, sii
se cambia para que sea una variable localvoid F() { int i = 1; if (i == 2) Console.WriteLine("reachable"); }
la
Console.WriteLine
invocación se considera accesible, aunque, en realidad, nunca se ejecutará.nota final
El bloque de un miembro de función o una función anónima siempre se considera accesible. Al evaluar sucesivamente las reglas de accesibilidad de cada instrucción en un bloque, se puede determinar la capacidad de acceso de cualquier instrucción determinada.
Ejemplo: en el código siguiente
void F(int x) { Console.WriteLine("start"); if (x < 0) Console.WriteLine("negative"); }
la capacidad de acceso del segundo
Console.WriteLine
se determina de la siguiente manera:
- La primera
Console.WriteLine
instrucción de expresión es accesible porque el bloque delF
método es accesible (§13.3).- El punto final de la primera
Console.WriteLine
instrucción de expresión es accesible porque esa instrucción es accesible (§13.7 y §13.3).- La
if
instrucción es accesible porque el punto final de la primeraConsole.WriteLine
instrucción de expresión es accesible (§13.7 y §13.3).- La segunda
Console.WriteLine
instrucción de expresión es accesible porque la expresión booleana de laif
instrucción no tiene el valorfalse
constante .ejemplo final
Hay dos situaciones en las que se trata de un error en tiempo de compilación para que el punto final de una instrucción sea accesible:
Dado que la
switch
instrucción no permite que una sección de modificador "pase" a la siguiente sección del modificador, se trata de un error en tiempo de compilación para que el punto final de la lista de instrucciones de una sección de modificador sea accesible. Si se produce este error, suele ser una indicación de que falta unabreak
instrucción.Es un error en tiempo de compilación para el punto final del bloque de un miembro de función o una función anónima que calcula un valor al que se puede acceder. Si se produce este error, normalmente es una indicación de que falta una
return
instrucción (§13.10.5).
13.3 Bloques
13.3.1 General
Un bloque permite que se escriban varias instrucciones en contextos donde se permite una única instrucción.
block
: '{' statement_list? '}'
;
Un bloque consta de una statement_list opcional (§13.3.2), entre llaves. Si se omite la lista de instrucciones, se dice que el bloque está vacío.
Un bloque puede contener instrucciones de declaración (§13.6). El ámbito de una variable local o constante declarada en un bloque es el bloque .
Se ejecuta un bloque como se indica a continuación:
- Si el bloque está vacío, el control se transfiere al punto final del bloque.
- Si el bloque no está vacío, el control se transfiere a la lista de instrucciones. Cuando y si el control llega al punto final de la lista de instrucciones, el control se transfiere al punto final del bloque.
La lista de instrucciones de un bloque es accesible si se puede acceder al propio bloque.
El punto final de un bloque es accesible si el bloque está vacío o si se puede acceder al punto final de la lista de instrucciones.
Un bloque que contiene una o varias yield
instrucciones (§13.15) se denomina bloque de iterador. Los bloques de iterador se usan para implementar miembros de función como iteradores (§15.14). Algunas restricciones adicionales se aplican a los bloques de iterador:
- Es un error en tiempo de compilación para que una
return
instrucción aparezca en un bloque de iterador (peroyield return
se permiten instrucciones). - Es un error en tiempo de compilación para que un bloque de iterador contenga un contexto no seguro (§23.2). Un bloque de iterador siempre define un contexto seguro, incluso cuando su declaración está anidada en un contexto no seguro.
13.3.2 Listas de instrucciones
Una lista de instrucciones consta de una o varias instrucciones escritas en secuencia. Las listas de instrucciones se producen en los bloquess (§13.3) y en switch_blocks (§13.8.3).
statement_list
: statement+
;
Una lista de instrucciones se ejecuta transfiriendo el control a la primera instrucción. Cuando y si el control llega al punto final de una instrucción, el control se transfiere a la instrucción siguiente. Cuando y si el control llega al punto final de la última instrucción, el control se transfiere al punto final de la lista de instrucciones.
Se puede acceder a una instrucción de una lista de instrucciones si se cumple al menos una de las siguientes condiciones:
- La instrucción es la primera instrucción y la propia lista de instrucciones es accesible.
- Se puede acceder al punto final de la instrucción anterior.
- La instrucción es una instrucción etiquetada y una instrucción accesible
goto
hace referencia a la etiqueta.
El punto final de una lista de instrucciones es accesible si se puede acceder al punto final de la última instrucción de la lista.
13.4 Instrucción vacía
Un empty_statement no hace nada.
empty_statement
: ';'
;
Se usa una instrucción vacía cuando no hay ninguna operación para realizar en un contexto en el que se requiere una instrucción.
La ejecución de una instrucción vacía simplemente transfiere el control al punto final de la instrucción. Por lo tanto, se puede acceder al punto final de una instrucción vacía si se puede acceder a la instrucción vacía.
Ejemplo: se puede usar una instrucción vacía al escribir una
while
instrucción con un cuerpo NULL:bool ProcessMessage() {...} void ProcessMessages() { while (ProcessMessage()) ; }
Además, se puede usar una instrucción vacía para declarar una etiqueta justo antes del cierre "
}
" de un bloque:void F(bool done) { ... if (done) { goto exit; } ... exit: ; }
ejemplo final
13.5 Instrucciones etiquetadas
Un labeled_statement permite que una instrucción tenga como prefijo una etiqueta. Las instrucciones etiquetadas se permiten en bloques, pero no se permiten como instrucciones insertadas.
labeled_statement
: identifier ':' statement
;
Una instrucción etiquetada declara una etiqueta con el nombre proporcionado por el identificador. El ámbito de una etiqueta es el bloque completo en el que se declara la etiqueta, incluidos los bloques anidados. Se trata de un error en tiempo de compilación para que dos etiquetas con el mismo nombre tengan ámbitos superpuestos.
Se puede hacer referencia a una etiqueta desde goto
instrucciones (§13.10.4) dentro del ámbito de la etiqueta.
Nota: Esto significa que
goto
las instrucciones pueden transferir el control dentro de bloques y fuera de bloques, pero nunca en bloques. nota final
Las etiquetas tienen su propio espacio de declaración y no interfieren con otros identificadores.
Ejemplo: El ejemplo
int F(int x) { if (x >= 0) { goto x; } x = -x; x: return x; }
es válido y usa el nombre x como un parámetro y una etiqueta.
ejemplo final
La ejecución de una instrucción etiquetada corresponde exactamente a la ejecución de la instrucción que sigue a la etiqueta.
Además de la accesibilidad proporcionada por el flujo normal de control, se puede acceder a una instrucción etiquetada si se hace referencia a la etiqueta mediante una goto
instrucción accesible, a menos que la goto
instrucción esté dentro del try
bloque o un catch
bloque de un try_statement que incluya un finally
bloque cuyo punto de conexión sea inaccesible y la instrucción etiquetada esté fuera del try_statement.
13.6 Declaraciones
13.6.1 General
Un declaration_statement declara una o varias variables locales, una o varias constantes locales o una función local. Las instrucciones de declaración se permiten en bloques y bloques switch, pero no se permiten como instrucciones insertadas.
declaration_statement
: local_variable_declaration ';'
| local_constant_declaration ';'
| local_function_declaration
;
Una variable local se declara mediante un local_variable_declaration (§13.6.2). Una constante local se declara mediante un local_constant_declaration (§13.6.3). Una función local se declara mediante un local_function_declaration (§13.6.4).
Los nombres declarados se introducen en el espacio de declaración envolvente más cercano (§7.3).
13.6.2 Declaraciones de variables locales
13.6.2.1 General
Un local_variable_declaration declara una o varias variables locales.
local_variable_declaration
: implicitly_typed_local_variable_declaration
| explicitly_typed_local_variable_declaration
| explicitly_typed_ref_local_variable_declaration
;
Las declaraciones con tipo implícito contienen la palabra clave contextual (§6.4.4) var
que da lugar a una ambigüedad sintáctica entre las tres categorías que se resuelven de la siguiente manera:
- Si no hay ningún tipo denominado
var
en el ámbito y la entrada coincide con implicitly_typed_local_variable_declaration , se elige; - De lo contrario, si un tipo denominado
var
está en el ámbito, implicitly_typed_local_variable_declaration no se considera una coincidencia posible.
Dentro de un local_variable_declaration cada variable se introduce mediante un declarador, que es uno de implicitly_typed_local_variable_declarator, explicitly_typed_local_variable_declarator o ref_local_variable_declarator para variables locales con tipo implícito, explícitamente tipadas y ref, respectivamente. El declarador define el nombre (identificador) y el valor inicial, si existe, de la variable introducida.
Si hay varios declaradores en una declaración, se procesan, incluidas las expresiones de inicialización, en orden de izquierda a derecha (§9.4.4.5).
Nota: Para una local_variable_declaration no se produce como una for_initializer (§13.9.4) o resource_acquisition (§13.14), este orden de izquierda a derecha es equivalente a que cada declarador esté dentro de un local_variable_declaration independiente. Por ejemplo:
void F() { int x = 1, y, z = x * 2; }
equivale a:
void F() { int x = 1; int y; int z = x * 2; }
nota final
El valor de una variable local se obtiene en una expresión mediante un simple_name (§12.8.4). Se asignará definitivamente una variable local (§9.4) en cada ubicación donde se obtenga su valor. Cada variable local introducida por un local_variable_declaration está inicialmente sin asignar (§9.4.3). Si un declarador tiene una expresión de inicialización, la variable local introducida se clasifica como asignada al final del declarador (§9.4.4.5).
El ámbito de una variable local introducida por un local_variable_declaration se define de la siguiente manera (§7.7):
- Si la declaración se produce como un for_initializer , el ámbito es el for_initializer, for_condition, for_iterator y embedded_statement (§13.9.4);
- Si la declaración se produce como un resource_acquisition , el ámbito es el bloque más externo de la expansión semánticamente equivalente de la using_statement (§13.14);
- De lo contrario, el ámbito es el bloque en el que se produce la declaración.
Es un error hacer referencia a una variable local por nombre en una posición textual que precede a su declarador o dentro de cualquier expresión inicializador dentro de su declarador. Dentro del ámbito de una variable local, se trata de un error en tiempo de compilación para declarar otra variable local, función local o constante con el mismo nombre.
El contexto ref-safe-context (§9.7.2) de una variable local ref es el contexto ref-safe-context de su inicialización variable_reference. El contexto ref-safe-of non-ref local variables es declaration-block.
13.6.2.2 Declaraciones de variables locales con tipo implícito
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
;
Un implicitly_typed_local_variable_declaration introduce una única variable local, un identificador. La expresión o variable_reference tendrá un tipo en tiempo de compilación, T
. La primera alternativa declara una variable con un valor inicial de expresión; su tipo es T?
cuando T
es un tipo de referencia que no acepta valores NULL; de lo contrario, su tipo es T
. La segunda alternativa declara una variable ref con un valor inicial de variable_reference; su tipo es ref T?
cuando T
es un tipo de ref
referencia que no acepta valores NULL; de lo contrario, su tipo es ref T
. (ref_kind se describe en §15.6.1).
Ejemplo:
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;
Las declaraciones de variables locales con tipo implícito anteriores son exactamente equivalentes a las siguientes declaraciones con tipo explícito:
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;
Las siguientes son declaraciones de variables locales con tipo implícito incorrectas:
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
ejemplo final
13.6.2.3 Declaraciones de variables locales con tipo explícito
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
;
Un explicity_typed_local_variable_declaration introduce una o varias variables locales con el tipo especificado.
Si existe un local_variable_initializer , su tipo será adecuado según las reglas de asignación simple (§12.21.2) o inicialización de matriz (§17.7) y su valor se asigna como valor inicial de la variable.
13.6.2.4 Declaraciones de variables locales ref tipadas explícitamente
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
;
La inicialización variable_reference tendrá un tipo de tipo y cumplirá los mismos requisitos que para una asignación de referencia (§12.21.3).
Si ref_kind es ref readonly
, los identificadores que se declaran son referencias a variables que se tratan como de solo lectura. De lo contrario, si ref_kind es ref
, los identificadores que se declaran son referencias a variables que se pueden escribir.
Se trata de un error en tiempo de compilación para declarar una variable local ref o una variable de un tipo, dentro de un ref struct
método declarado con el method_modifier async
, o dentro de un iterador (§15.14).
13.6.3 Declaraciones de constante local
Un local_constant_declaration declara una o varias constantes locales.
local_constant_declaration
: 'const' type constant_declarators
;
constant_declarators
: constant_declarator (',' constant_declarator)*
;
constant_declarator
: identifier '=' constant_expression
;
El tipo de un local_constant_declaration especifica el tipo de las constantes introducidas por la declaración. El tipo va seguido de una lista de constant_declarators, cada una de las cuales presenta una nueva constante. Un constant_declarator consta de un identificador que denomina la constante, seguido de un token "=
", seguido de un constant_expression (§12.23) que proporciona el valor de la constante.
El tipo y constant_expression de una declaración constante local seguirán las mismas reglas que las de una declaración de miembro constante (§15.4).
El valor de una constante local se obtiene en una expresión mediante un simple_name (§12.8.4).
El ámbito de una constante local es el bloque en el que se produce la declaración. Es un error hacer referencia a una constante local en una posición textual que precede al final de su constant_declarator.
Una declaración de constante local que declara varias constantes es equivalente a varias declaraciones de constantes únicas con el mismo tipo.
13.6.4 Declaraciones de función local
Un local_function_declaration declara una función local.
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 ';'
;
Nota gramatical: Al reconocer un local_function_body si se aplican tanto el null_conditional_invocation_expression como las alternativas de expresión , se elegirá la primera. (§15.6.1)
Ejemplo: hay dos casos de uso comunes para las funciones locales: métodos de iterador y métodos asincrónicos. En los métodos de iterador, las excepciones solo se observan al llamar a código que enumera la secuencia devuelta. En los métodos asincrónicos, las excepciones solo se observan cuando se espera la tarea devuelta. En el ejemplo siguiente se muestra la separación de la validación de parámetros de la implementación de iteradores mediante una función local:
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; } } }
ejemplo final
A menos que se especifique lo contrario, la semántica de todos los elementos gramaticales es la misma que para method_declaration (§15.6.1), lea en el contexto de una función local en lugar de un método.
El identificador de un local_function_declaration será único en su ámbito de bloque declarado, incluidos los espacios de declaración de variables locales envolventes. Una consecuencia de esto es que no se permiten los local_function_declarationsobrecargados.
Un local_function_declaration puede incluir un async
modificador (§15.15) y un unsafe
modificador (§23.1). Si la declaración incluye el async
modificador, el tipo de valor devuelto será void
o un «TaskType»
tipo (§15.15.1). Si la declaración incluye el static
modificador, la función es una función local estática; de lo contrario, es una función local no estática. Se trata de un error en tiempo de compilación para que type_parameter_list o parameter_list contengan atributos. Si la función local se declara en un contexto no seguro (§23.2), la función local puede incluir código no seguro, incluso si la declaración de función local no incluye el unsafe
modificador.
Una función local se declara en el ámbito de bloque. Una función local no estática puede capturar variables del ámbito envolvente, mientras que una función local estática no debe (por lo que no tiene acceso a variables locales, parámetros, funciones locales no estáticas o this
). Es un error en tiempo de compilación si el cuerpo de una función local no estática lee una variable capturada, pero no se asigna definitivamente antes de cada llamada a la función. El compilador determinará qué variables se asignan definitivamente al devolver (§9.4.4.33).
Cuando el tipo de this
es un tipo de estructura, es un error en tiempo de compilación para que el cuerpo de una función local acceda this
a . Esto es cierto si el acceso es explícito (como en this.x
) o implícito (como en donde x
x
es un miembro de instancia de la estructura). Esta regla solo prohíbe este acceso y no afecta a si la búsqueda de miembros da como resultado un miembro de la estructura.
Es un error en tiempo de compilación para que el cuerpo de la función local contenga una goto
instrucción, una break
instrucción o una continue
instrucción cuyo destino está fuera del cuerpo de la función local.
Nota: las reglas anteriores para
this
ygoto
reflejan las reglas para las funciones anónimas en §12.19.3. nota final
Se puede llamar a una función local desde un punto léxico antes de su declaración. Sin embargo, es un error en tiempo de compilación para que la función se declare léxicamente antes de la declaración de una variable usada en la función local (§7.7).
Es un error en tiempo de compilación para que una función local declare un parámetro, un parámetro de tipo o una variable local con el mismo nombre que uno declarado en cualquier espacio de declaración de variable local envolvente.
Los cuerpos de función locales siempre son accesibles. El punto de conexión de una declaración de función local es accesible si se puede acceder al punto inicial de la declaración de función local.
Ejemplo: En el ejemplo siguiente, el cuerpo de
L
es accesible aunque el punto inicial deL
no sea accesible. Dado que el punto inicial deL
no es accesible, la instrucción que sigue al punto de conexión deL
no es accesible: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; } }
En otras palabras, la ubicación de una declaración de función local no afecta a la accesibilidad de las instrucciones de la función contenedora. ejemplo final
Si el tipo del argumento para una función local es dynamic
, la función a la que se va a llamar se resolverá en tiempo de compilación, no en tiempo de ejecución.
Una función local no se usará en un árbol de expresión.
Una función local estática
- Puede hacer referencia a miembros estáticos, parámetros de tipo, definiciones constantes y funciones locales estáticas desde el ámbito envolvente.
- No hará referencia a
this
nibase
a los miembros de instancia de una referencia implícitathis
, ni variables locales, parámetros ni funciones locales no estáticas desde el ámbito envolvente. Sin embargo, todas estas se permiten en unanameof()
expresión.
13.7 Instrucciones expression
Un expression_statement evalúa una expresión determinada. El valor calculado por la expresión, si existe, se descarta.
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
;
No todas las expresiones se permiten como instrucciones.
Nota: En concreto, las expresiones como
x + y
yx == 1
, que simplemente calculan un valor (que se descartará), no se permiten como instrucciones. nota final
La ejecución de un expression_statement evalúa la expresión contenida y, a continuación, transfiere el control al punto final del expression_statement. El punto final de un expression_statement es accesible si se puede acceder a ese expression_statement .
13.8 Instrucciones selection
13.8.1 General
Las instrucciones de selección seleccionan una de las posibles instrucciones para su ejecución en función del valor de alguna expresión.
selection_statement
: if_statement
| switch_statement
;
13.8.2 Instrucción if
La if
instrucción selecciona una instrucción para su ejecución en función del valor de una expresión booleana.
if_statement
: 'if' '(' boolean_expression ')' embedded_statement
| 'if' '(' boolean_expression ')' embedded_statement
'else' embedded_statement
;
Una else
parte está asociada con el elemento léxico más cercano anterior if
permitido por la sintaxis.
Ejemplo: Por lo tanto, una
if
instrucción del formularioif (x) if (y) F(); else G();
es equivalente a
if (x) { if (y) { F(); } else { G(); } }
ejemplo final
Se ejecuta una if
instrucción como se indica a continuación:
- Se evalúa el boolean_expression (§12.24).
- Si la expresión booleana produce
true
, el control se transfiere a la primera instrucción insertada. Cuando y si el control llega al punto final de esa instrucción, el control se transfiere al punto final de laif
instrucción. - Si la expresión booleana produce
false
y si hay unaelse
parte presente, el control se transfiere a la segunda instrucción insertada. Cuando y si el control llega al punto final de esa instrucción, el control se transfiere al punto final de laif
instrucción. - Si la expresión booleana produce
false
y si unaelse
parte no está presente, el control se transfiere al punto final de laif
instrucción.
La primera instrucción insertada de una if
instrucción es accesible si la if
instrucción es accesible y la expresión booleana no tiene el valor false
constante .
La segunda instrucción insertada de una if
instrucción, si está presente, es accesible si la if
instrucción es accesible y la expresión booleana no tiene el valor true
constante .
El punto final de una if
instrucción es accesible si se puede acceder al punto final de al menos una de sus instrucciones incrustadas. Además, el punto final de una if
instrucción sin else
elemento es accesible si la if
instrucción es accesible y la expresión booleana no tiene el valor true
constante .
13.8.3 Instrucción switch
La switch
instrucción selecciona para ejecutar una lista de instrucciones que tiene una etiqueta de modificador asociada que corresponde al valor de la expresión 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
;
Un switch_statement consta de la palabra clave switch
, seguido de una expresión entre paréntesis (denominada expresión switch), seguida de un switch_block. El switch_block consta de cero o más switch_sections, entre llaves. Cada switch_section consta de una o varias switch_labelseguidas de un statement_list (§13.3.2). Cada switch_label que contiene case
tiene un patrón asociado (§11) con el que se prueba el valor de la expresión switch. Si case_guard está presente, su expresión se podrá convertir implícitamente en el tipo bool
y esa expresión se evaluará como una condición adicional para que el caso se considere satisfecho.
La expresión switch establece el tipo de gobernanza de una switch
instrucción.
- Si el tipo de la expresión switch es
sbyte
,byte
,short
uint
int
ulong
bool
long
ushort
char
,string
o un enum_type, o si es el tipo de valor que acepta valores NULL correspondiente a uno de estos tipos, ese es el tipo de gobierno de laswitch
instrucción. - De lo contrario, si existe exactamente una conversión implícita definida por el usuario del tipo de la expresión switch a uno de los siguientes tipos de gobierno posibles:
sbyte
,byte
,ushort
long
int
short
ulong
char
uint
ostring
, un tipo de valor que acepta valores NULL correspondiente a uno de esos tipos, el tipo convertido es el tipo de gobierno de laswitch
instrucción. - De lo contrario, el tipo de gobernanza de la
switch
instrucción es el tipo de la expresión switch. Se trata de un error si no existe este tipo.
Puede haber como máximo una default
etiqueta en una switch
instrucción .
Se trata de un error si el patrón de cualquier etiqueta de modificador no es aplicable (§11.2.1) al tipo de la expresión de entrada.
Se trata de un error si el patrón de cualquier etiqueta de modificador está subsumado por (§11.3) el conjunto de patrones de etiquetas de conmutador anteriores de la instrucción switch que no tienen una protección de mayúsculas y minúsculas o cuya protección de mayúsculas y minúsculas es una expresión constante con el valor true.
Ejemplo:
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. }
ejemplo final
Se ejecuta una switch
instrucción como se indica a continuación:
- La expresión switch se evalúa y convierte en el tipo de gobierno.
- El control se transfiere según el valor de la expresión switch convertida:
- El primer patrón léxico del conjunto de etiquetas de
case
la mismaswitch
instrucción que coincide con el valor de la expresión switch y para el que la expresión de protección está ausente o se evalúa como true, hace que el control se transfiera a la lista de instrucciones después de la etiqueta coincidentecase
. - De lo contrario, si hay una
default
etiqueta presente, el control se transfiere a la lista de instrucciones después de ladefault
etiqueta. - De lo contrario, el control se transfiere al punto final de la
switch
instrucción .
- El primer patrón léxico del conjunto de etiquetas de
Nota: No se define el orden en el que se coinciden los patrones en tiempo de ejecución. Se permite que un compilador (pero no necesario) coincida con patrones desordenados y reutilizar los resultados de patrones ya coincidentes para calcular el resultado de la coincidencia de otros patrones. Sin embargo, el compilador es necesario para determinar el primer patrón léxico que coincide con la expresión y para la que la cláusula guard está ausente o se evalúa como
true
. nota final
Si se puede acceder al punto final de la lista de instrucciones de una sección switch, se produce un error en tiempo de compilación. Esto se conoce como regla de "no caer a través".
Ejemplo: El ejemplo
switch (i) { case 0: CaseZero(); break; case 1: CaseOne(); break; default: CaseOthers(); break; }
es válido porque ninguna sección de conmutador tiene un punto de conexión accesible. A diferencia de C y C++, la ejecución de una sección switch no puede "pasarse" a la siguiente sección del modificador y el ejemplo
switch (i) { case 0: CaseZero(); case 1: CaseZeroOrOne(); default: CaseAny(); }
produce un error en tiempo de compilación. Cuando la ejecución de una sección switch va a ir seguida de la ejecución de otra sección switch, se usará una instrucción o
goto default
explícitagoto case
:switch (i) { case 0: CaseZero(); goto case 1; case 1: CaseZeroOrOne(); goto default; default: CaseAny(); break; }
ejemplo final
Se permiten varias etiquetas en una switch_section.
Ejemplo: El ejemplo
switch (i) { case 0: CaseZero(); break; case 1: CaseOne(); break; case 2: default: CaseTwo(); break; }
es válido. El ejemplo no infringe la regla "no fall through" porque las etiquetas
case 2:
ydefault:
forman parte del mismo switch_section.ejemplo final
Nota: La regla "sin caída" impide una clase común de errores que se producen en C y C++ cuando
break
las instrucciones se omiten accidentalmente. Por ejemplo, las secciones de laswitch
instrucción anterior se pueden invertir sin afectar al comportamiento de la instrucción :switch (i) { default: CaseAny(); break; case 1: CaseZeroOrOne(); goto default; case 0: CaseZero(); goto case 1; }
nota final
Nota: La lista de instrucciones de una sección switch normalmente termina en una
break
instrucción ,goto case
ogoto default
, pero se permite cualquier construcción que represente el punto final de la lista de instrucciones inaccesible. Por ejemplo, se sabe que unawhile
instrucción controlada por la expresióntrue
booleana nunca alcanza su punto final. Del mismo modo, unathrow
instrucción oreturn
siempre transfiere el control en otro lugar y nunca alcanza su punto de conexión. Por lo tanto, el ejemplo siguiente es válido:switch (i) { case 0: while (true) { F(); } case 1: throw new ArgumentException(); case 2: return; }
nota final
Ejemplo: el tipo de gobernanza de una
switch
instrucción puede ser el tipostring
. Por ejemplo:void DoCommand(string command) { switch (command.ToLower()) { case "run": DoRun(); break; case "save": DoSave(); break; case "quit": DoQuit(); break; default: InvalidCommand(command); break; } }
ejemplo final
Nota: Al igual que los operadores de igualdad de cadenas (§12.12.8), la
switch
instrucción distingue mayúsculas de minúsculas y ejecutará una sección de conmutador determinada solo si la cadena de expresión switch coincide exactamente con unacase
constante de etiqueta. nota final Cuando el tipo de gobernanza de unaswitch
instrucción esstring
o un tipo de valor que acepta valores NULL, el valornull
se permite como unacase
constante de etiqueta.
Los statement_listde un switch_block pueden contener instrucciones de declaración (§13.6). El ámbito de una variable local o constante declarada en un bloque switch es el bloque switch.
Se puede acceder a una etiqueta switch si se cumple al menos una de las siguientes opciones:
- La expresión switch es un valor constante y cualquiera de los dos
- la etiqueta es un
case
cuyo patrón coincidiría (§11.2.1) ese valor y la protección de la etiqueta está ausente o no una expresión constante con el valor false; o bien - es una
default
etiqueta y ninguna sección switch contiene una etiqueta de mayúsculas y minúsculas cuyo patrón coincidiría con ese valor y cuya protección está ausente o una expresión constante con el valor true.
- la etiqueta es un
- La expresión switch no es un valor constante y tampoco
- la etiqueta es sin
case
protección o con una protección cuyo valor no es la constante false; o - es una
default
etiqueta y- el conjunto de patrones que aparecen entre los casos de la instrucción switch que no tienen guardias o tienen guardias cuyo valor es la constante true, no es exhaustivo (§11.4) para el tipo de regulación del modificador; o
- el tipo de control switch es un tipo que acepta valores NULL y el conjunto de patrones que aparecen entre los casos de la instrucción switch que no tienen guardias o tienen guardias cuyo valor es la constante true no contiene un patrón que coincida con el valor
null
.
- la etiqueta es sin
- Se hace referencia a la etiqueta del modificador mediante una instrucción o
goto default
accesiblegoto case
.
La lista de instrucciones de una sección de conmutador determinada es accesible si la switch
instrucción es accesible y la sección switch contiene una etiqueta de conmutador accesible.
El punto final de una switch
instrucción es accesible si se puede acceder a la instrucción switch y al menos se cumple una de las siguientes condiciones:
- La
switch
instrucción contiene una instrucción accesiblebreak
que sale de laswitch
instrucción . - Ninguna
default
etiqueta está presente y tampoco- La expresión switch es un valor no constante y el conjunto de patrones que aparecen entre los casos de la instrucción switch que no tienen guardias o tienen guardias cuyo valor es la constante true, no es exhaustivo (§11.4) para el tipo de regulación del conmutador.
- La expresión switch es un valor no constante de un tipo que acepta valores NULL y ningún patrón que aparece entre los casos de la instrucción switch que no tienen guardias o tienen guardias cuyo valor es la constante true coincidiría con el valor
null
. - La expresión switch es un valor constante y ninguna
case
etiqueta sin una protección o cuya protección es la constante true coincidiría con ese valor.
Ejemplo: El código siguiente muestra un uso concisa de la
when
cláusula :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"; } }
El caso var coincide con
null
, la cadena vacía o cualquier cadena que contenga solo espacio en blanco. ejemplo final
13.9 Instrucciones de iteración
13.9.1 General
Las instrucciones de iteración ejecutan repetidamente una instrucción insertada.
iteration_statement
: while_statement
| do_statement
| for_statement
| foreach_statement
;
13.9.2 Instrucción while
La while
instrucción ejecuta condicionalmente una instrucción incrustada cero o más veces.
while_statement
: 'while' '(' boolean_expression ')' embedded_statement
;
Se ejecuta una while
instrucción como se indica a continuación:
- Se evalúa el boolean_expression (§12.24).
- Si la expresión booleana produce
true
, el control se transfiere a la instrucción insertada. Cuando y si el control llega al punto final de la instrucción incrustada (posiblemente desde la ejecución de unacontinue
instrucción), el control se transfiere al principio de lawhile
instrucción. - Si la expresión booleana produce
false
, el control se transfiere al punto final de lawhile
instrucción .
Dentro de la instrucción insertada de una while
instrucción, se puede usar una break
instrucción (§13.10.2) para transferir el control al punto final de la while
instrucción (por lo tanto, la iteración final de la instrucción incrustada) y una continue
instrucción (§13.10.3) se puede usar para transferir el control al punto final de la instrucción incrustada (realizando así otra iteración de la while
instrucción).
La instrucción insertada de una while
instrucción es accesible si la while
instrucción es accesible y la expresión booleana no tiene el valor false
constante .
El punto final de una while
instrucción es accesible si se cumple al menos uno de los siguientes elementos:
- La
while
instrucción contiene una instrucción accesiblebreak
que sale de lawhile
instrucción . - La
while
instrucción es accesible y la expresión booleana no tiene el valortrue
constante .
13.9.3 Instrucción do
La do
instrucción ejecuta condicionalmente una instrucción incrustada una o varias veces.
do_statement
: 'do' embedded_statement 'while' '(' boolean_expression ')' ';'
;
Se ejecuta una do
instrucción como se indica a continuación:
- El control se transfiere a la instrucción insertada.
- Cuando y si el control llega al punto final de la instrucción insertada (posiblemente desde la ejecución de una
continue
instrucción), se evalúa el boolean_expression (§12.24). Si la expresión booleana producetrue
, el control se transfiere al principio de lado
instrucción . De lo contrario, el control se transfiere al punto final de lado
instrucción .
Dentro de la instrucción insertada de una do
instrucción, se puede usar una break
instrucción (§13.10.2) para transferir el control al punto final de la do
instrucción (por lo tanto, la iteración final de la instrucción incrustada) y una continue
instrucción (§13.10.3) se puede usar para transferir el control al punto final de la instrucción incrustada (realizando así otra iteración de la do
instrucción).
La instrucción insertada de una do
instrucción es accesible si la do
instrucción es accesible.
El punto final de una do
instrucción es accesible si se cumple al menos uno de los siguientes elementos:
- La
do
instrucción contiene una instrucción accesiblebreak
que sale de lado
instrucción . - El punto final de la instrucción insertada es accesible y la expresión booleana no tiene el valor
true
constante .
13.9.4 Instrucción for
La for
instrucción evalúa una secuencia de expresiones de inicialización y, a continuación, mientras que una condición es true, ejecuta repetidamente una instrucción incrustada y evalúa una secuencia de expresiones de iteración.
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)*
;
El for_initializer, si está presente, consta de un local_variable_declaration (§13.6.2) o una lista de statement_expressions (§13.7) separados por comas. El ámbito de una variable local declarada por un for_initializer es el for_initializer, for_condition, for_iterator y embedded_statement.
El for_condition, si está presente, será un boolean_expression (§12.24).
El for_iterator, si está presente, consta de una lista de statement_expressions (§13.7) separados por comas.
Se ejecuta una for
instrucción como se indica a continuación:
- Si hay un for_initializer presente, los inicializadores de variable o las expresiones de instrucción se ejecutan en el orden en que se escriben. Este paso solo se realiza una vez.
- Si hay un for_condition presente, se evalúa.
- Si el for_condition no está presente o si la evaluación produce
true
, el control se transfiere a la instrucción insertada. Cuando y si el control llega al punto final de la instrucción insertada (posiblemente desde la ejecución de unacontinue
instrucción), las expresiones de la for_iterator, si las hay, se evalúan en secuencia y, a continuación, se realiza otra iteración, empezando por la evaluación del for_condition en el paso anterior. - Si el for_condition está presente y la evaluación produce
false
, el control se transfiere al punto final de lafor
instrucción .
Dentro de la instrucción insertada de una for
instrucción, se puede usar una break
instrucción (§13.10.2) para transferir el control al punto final de la for
instrucción (por lo tanto, la iteración final de la instrucción insertada) y una continue
instrucción (§13.10.3) se puede usar para transferir el control al punto final de la instrucción incrustada (ejecutando así el for_iterator y realizando otra iteración de la for
instrucción, a partir del for_condition).
La instrucción insertada de una for
instrucción es accesible si se cumple una de las siguientes condiciones:
- La
for
instrucción es accesible y no hay ningún for_condition presente. - La
for
instrucción es accesible y hay una for_condition presente y no tiene el valorfalse
constante .
El punto final de una for
instrucción es accesible si se cumple al menos uno de los siguientes elementos:
- La
for
instrucción contiene una instrucción accesiblebreak
que sale de lafor
instrucción . - La
for
instrucción es accesible y hay una for_condition presente y no tiene el valortrue
constante .
13.9.5 Instrucción foreach
La foreach
instrucción enumera los elementos de una colección, ejecutando una instrucción insertada para cada elemento de la colección.
foreach_statement
: 'foreach' '(' ref_kind? local_variable_type identifier 'in'
expression ')' embedded_statement
;
El local_variable_type e identificador de una instrucción foreach declaran la variable de iteración de la instrucción . Si el var
identificador se proporciona como el local_variable_type y ningún tipo denominado var
está en el ámbito, se dice que la variable de iteración es una variable de iteración con tipo implícito y su tipo se toma para ser el tipo de elemento de la foreach
instrucción, como se especifica a continuación.
Si el foreach_statement contiene o ninguno ref
de ellos y readonly
, la variable de iteración denota una variable que se trata como de solo lectura. De lo contrario, si foreach_statement contiene ref
sin readonly
, la variable de iteración denota una variable que se puede escribir.
La variable de iteración corresponde a una variable local con un ámbito que se extiende a través de la instrucción insertada. Durante la ejecución de una foreach
instrucción, la variable de iteración representa el elemento de colección para el que se está realizando actualmente una iteración. Si la variable de iteración denota una variable de solo lectura, se produce un error en tiempo de compilación si la instrucción insertada intenta modificarla (a través de la asignación o los ++
operadores y --
) o pasarla como un parámetro de referencia o salida.
En lo siguiente, para mayor brevedad, IEnumerable
, IEnumerator
IEnumerable<T>
y IEnumerator<T>
haga referencia a los tipos correspondientes System.Collections
en los espacios de nombres y System.Collections.Generic
.
El procesamiento en tiempo de compilación de una foreach
instrucción determina primero el tipo de colección, el tipo de enumerador y el tipo de iteración de la expresión. Esta determinación continúa de la siguiente manera:
- Si el tipo
X
de expresión es un tipo de matriz, hay una conversión de referencia implícita de X a laIEnumerable
interfaz (ya queSystem.Array
implementa esta interfaz). El tipo de colección es laIEnumerable
interfaz, el tipo de enumerador es laIEnumerator
interfaz y el tipo de iteración es el tipo de elemento del tipoX
de matriz . - Si el tipo
X
de expresión esdynamic
entonces hay una conversión implícita de expresión a laIEnumerable
interfaz (§10.2.10). El tipo de colección es laIEnumerable
interfaz y el tipo de enumerador es laIEnumerator
interfaz. Si elvar
identificador se proporciona como el local_variable_type , el tipo de iteración esdynamic
, de lo contrario, esobject
. - De lo contrario, determine si el tipo
X
tiene un método adecuadoGetEnumerator
:- Realice la búsqueda de miembros en el tipo
X
con identificadorGetEnumerator
y sin argumentos de tipo. Si la búsqueda de miembros no produce una coincidencia, o genera una ambigüedad, o genera una coincidencia que no es un grupo de métodos, compruebe si hay una interfaz enumerable como se describe a continuación. Se recomienda emitir una advertencia si la búsqueda de miembros genera algo excepto un grupo de métodos o ninguna coincidencia. - Realice la resolución de sobrecargas mediante el grupo de métodos resultante y una lista de argumentos vacía. Si la resolución de sobrecargas no da lugar a ningún método aplicable, da como resultado una ambigüedad o da como resultado un único método mejor, pero ese método es estático o no público, compruebe si hay una interfaz enumerable como se describe a continuación. Se recomienda emitir una advertencia si la resolución de sobrecarga genera algo excepto un método de instancia pública inequívoca o ningún método aplicable.
- Si el tipo
E
de valor devuelto delGetEnumerator
método no es una clase, estructura o tipo de interfaz, se genera un error y no se realizan pasos adicionales. - La búsqueda de miembros se realiza con
E
el identificadorCurrent
y sin argumentos de tipo. Si la búsqueda de miembros no produce ninguna coincidencia, el resultado es un error o el resultado es cualquier cosa excepto una propiedad de instancia pública que permita la lectura, se produce un error y no se realizan más pasos. - La búsqueda de miembros se realiza con
E
el identificadorMoveNext
y sin argumentos de tipo. Si la búsqueda de miembros no produce ninguna coincidencia, el resultado es un error o el resultado es cualquier cosa excepto un grupo de métodos, se genera un error y no se realizan más pasos. - La resolución de sobrecarga se realiza en el grupo de métodos con una lista de argumentos vacía. Si la resolución de sobrecargas no da lugar a ningún método aplicable, da como resultado una ambigüedad o da como resultado un único método mejor, pero ese método es estático o no público, o su tipo de valor devuelto no
bool
es , se genera un error y no se realizan pasos adicionales. - El tipo de colección es
X
, el tipo de enumerador esE
y el tipo de iteración es el tipo de laCurrent
propiedad . LaCurrent
propiedad puede incluir elref
modificador, en cuyo caso, la expresión devuelta es un variable_reference (§9.5) que es opcionalmente de solo lectura.
- Realice la búsqueda de miembros en el tipo
- De lo contrario, compruebe si hay una interfaz enumerable:
- Si entre todos los tipos
Tᵢ
para los que hay una conversión implícita deX
aIEnumerable<Tᵢ>
, hay un tipoT
único, de modo queT
nodynamic
es y para el restoTᵢ
hay una conversión implícita deIEnumerable<T>
aIEnumerable<Tᵢ>
, el tipo de colección es la interfazIEnumerable<T>
, el tipo de enumerador es la interfazIEnumerator<T>
y el tipo de iteración esT
. - De lo contrario, si hay más de un tipo de este tipo
T
, se produce un error y no se realizan pasos adicionales. - De lo contrario, si hay una conversión implícita de
X
a laSystem.Collections.IEnumerable
interfaz, el tipo de colección es esta interfaz, el tipo de enumerador es la interfazSystem.Collections.IEnumerator
y el tipo de iteración esobject
. - De lo contrario, se produce un error y no se realizan pasos adicionales.
- Si entre todos los tipos
Los pasos anteriores, si se realiza correctamente, generan de forma inequívoca un tipo de colección , el tipo C
de enumerador y el tipo E
T
de iteración , ref T
o ref readonly T
. Instrucción foreach
del formulario
foreach (V v in x) «embedded_statement»
es equivalente a:
{
E e = ((C)(x)).GetEnumerator();
try
{
while (e.MoveNext())
{
V v = (V)(T)e.Current;
«embedded_statement»
}
}
finally
{
... // Dispose e
}
}
La variable e
no es visible para la expresión o accesible para la expresión x
o la instrucción insertada o cualquier otro código fuente del programa. La variable v
es de solo lectura en la instrucción insertada. Si no hay una conversión explícita (§10.3) de T
(el tipo de iteración) a V
(el local_variable_type de la foreach
instrucción ), se genera un error y no se realizan pasos adicionales.
Cuando la variable de iteración es una variable de referencia (§9.7), una foreach
instrucción del formulario
foreach (ref V v in x) «embedded_statement»
es equivalente a:
{
E e = ((C)(x)).GetEnumerator();
try
{
while (e.MoveNext())
{
ref V v = ref e.Current;
«embedded_statement»
}
}
finally
{
... // Dispose e
}
}
La variable e
no es visible o accesible para la expresión x
o la instrucción insertada o cualquier otro código fuente del programa. La variable v
de referencia es de lectura y escritura en la instrucción insertada, pero v
no se reasignará a la referencia (§12.21.3). Si no hay una conversión de identidad (§10.2.2) de T
(el tipo de iteración) a V
(el local_variable_type de la foreach
instrucción), se genera un error y no se realizan pasos adicionales.
Una foreach
instrucción del formulario foreach (ref readonly V v in x) «embedded_statement»
tiene un formato equivalente similar, pero la variable v
de referencia está ref readonly
en la instrucción insertada y, por tanto, no se puede reasignar o reasignar.
Nota: Si
x
tiene el valornull
, se produce unaSystem.NullReferenceException
excepción en tiempo de ejecución. nota final
Se permite que una implementación implemente una foreach_statement determinada de forma diferente; por ejemplo, por motivos de rendimiento, siempre que el comportamiento sea coherente con la expansión anterior.
La colocación de v
dentro del while
bucle es importante para la forma en que se captura (§12.19.6.2) por cualquier función anónima que se produzca en el embedded_statement.
Ejemplo:
int[] values = { 7, 9, 13 }; Action f = null; foreach (var value in values) { if (f == null) { f = () => Console.WriteLine("First value: " + value); } } f();
Si
v
en el formato expandido se declarase fuera delwhile
bucle, se compartiría entre todas las iteraciones y su valor después delfor
bucle sería el valor final,13
, que es lo que la invocación def
imprimiría. En su lugar, dado que cada iteración tiene su propia variablev
, la capturada porf
en la primera iteración seguirá manteniendo el valor7
, que es lo que se imprimirá. (Tenga en cuenta que las versiones anteriores de C# se declararonv
fuera delwhile
bucle).ejemplo final
El cuerpo del finally
bloque se construye según los pasos siguientes:
Si hay una conversión implícita de
E
a laSystem.IDisposable
interfaz,Si
E
es un tipo de valor que no acepta valores NULL, lafinally
cláusula se expande al equivalente semántico de:finally { ((System.IDisposable)e).Dispose(); }
De lo contrario, la
finally
cláusula se expande al equivalente semántico de:finally { System.IDisposable d = e as System.IDisposable; if (d != null) { d.Dispose(); } }
excepto que si
E
es un tipo de valor o un parámetro de tipo creado en una instancia de un tipo de valor, la conversión dee
enSystem.IDisposable
no hará que se produzca la conversión boxing.
De lo contrario, si
E
es un tipo sellado, lafinally
cláusula se expande a un bloque vacío:finally {}
De lo contrario, la
finally
cláusula se expande a:finally { System.IDisposable d = e as System.IDisposable; if (d != null) { d.Dispose(); } }
La variable d
local no es visible ni accesible para ningún código de usuario. En concreto, no entra en conflicto con ninguna otra variable cuyo ámbito incluya el finally
bloque.
El orden en el que foreach
atraviesa los elementos de una matriz es el siguiente: Para los elementos de matrices unidimensionales se recorren en orden de índice creciente, empezando por el índice 0 y finalizando con el índice Length – 1
. En el caso de las matrices multidimensionales, los elementos se recorren de forma que los índices de la dimensión situada más a la derecha aumentan primero, luego la siguiente dimensión izquierda, etc. a la izquierda.
Ejemplo: en el ejemplo siguiente se imprime cada valor de una matriz bidimensional, en orden de elemento:
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(); } }
La salida generada es la siguiente:
1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9
ejemplo final
Ejemplo: En el ejemplo siguiente
int[] numbers = { 1, 3, 5, 7, 9 }; foreach (var n in numbers) { Console.WriteLine(n); }
el tipo de
n
se deduce comoint
, el tipo de iteración denumbers
.ejemplo final
13.10 Instrucciones jump
13.10.1 General
Las instrucciones jump transfieren incondicionalmente el control.
jump_statement
: break_statement
| continue_statement
| goto_statement
| return_statement
| throw_statement
;
La ubicación a la que una instrucción jump transfiere el control se denomina destino de la instrucción jump.
Cuando se produce una instrucción jump dentro de un bloque y el destino de esa instrucción jump está fuera de ese bloque, se dice que la instrucción jump sale del bloque. Aunque una instrucción jump puede transferir el control fuera de un bloque, nunca puede transferir el control a un bloque.
La ejecución de instrucciones de salto es complicada por la presencia de instrucciones intermedias try
. En ausencia de estas try
instrucciones, una instrucción jump transfiere incondicionalmente el control de la instrucción jump a su destino. En presencia de estas instrucciones intermedias try
, la ejecución es más compleja. Si la instrucción jump sale de uno o varios try
bloques con bloques asociados finally
, el control se transfiere inicialmente al finally
bloque de la instrucción más try
interna. Cuando y si el control llega al punto final de un finally
bloque, el control se transfiere al bloque de la finally
siguiente instrucción envolvente try
. Este proceso se repite hasta que se han ejecutado los finally
bloques de todas las instrucciones try
intermedias.
Ejemplo: en el código siguiente
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"); } }
Los
finally
bloques asociados a dostry
instrucciones se ejecutan antes de transferir el control al destino de la instrucción jump. La salida generada es la siguiente:Before break Innermost finally block Outermost finally block After break
ejemplo final
13.10.2 Instrucción break
La break
instrucción sale de la instrucción más cercana que incluye switch
, while
, do
, for
o foreach
.
break_statement
: 'break' ';'
;
El destino de una break
instrucción es el punto final de la instrucción más cercana que incluye switch
, while
, do
, for
o foreach
. Si una break
instrucción no está incluida en una switch
instrucción , while
, do
, o for
foreach
, se produce un error en tiempo de compilación.
Cuando varias switch
instrucciones , while
, do
, for
o foreach
se anidan entre sí, una break
instrucción solo se aplica a la instrucción más interna. Para transferir el control entre varios niveles de anidamiento, se usará una goto
instrucción (§13.10.4).
Una break
instrucción no puede salir de un finally
bloque (§13.11). Cuando se produce una break
instrucción dentro de un finally
bloque, el destino de la break
instrucción estará dentro del mismo finally
bloque; de lo contrario, se produce un error en tiempo de compilación.
Se ejecuta una break
instrucción como se indica a continuación:
- Si la instrucción sale de uno o varios
try
bloques con bloques asociadosfinally
, elbreak
control se transfiere inicialmente alfinally
bloque de la instrucción mástry
interna. Cuando y si el control llega al punto final de unfinally
bloque, el control se transfiere al bloque de lafinally
siguiente instrucción envolventetry
. Este proceso se repite hasta que se han ejecutado losfinally
bloques de todas las instruccionestry
intermedias. - El control se transfiere al destino de la
break
instrucción .
Dado que una break
instrucción transfiere incondicionalmente el control en otro lugar, el punto final de una break
instrucción nunca es accesible.
13.10.3 Instrucción continue
La continue
instrucción inicia una nueva iteración de la instrucción envolvente while
más cercana, , do
, for
o foreach
.
continue_statement
: 'continue' ';'
;
El destino de una continue
instrucción es el punto final de la instrucción incrustada de la instrucción insertada más cercana que incluye while
, do
, o for
foreach
. Si una continue
instrucción no está incluida en una while
instrucción , do
, for
o foreach
, se produce un error en tiempo de compilación.
Cuando varias while
instrucciones , do
, for
o foreach
se anidan entre sí, una continue
instrucción solo se aplica a la instrucción más interna. Para transferir el control entre varios niveles de anidamiento, se usará una goto
instrucción (§13.10.4).
Una continue
instrucción no puede salir de un finally
bloque (§13.11). Cuando se produce una continue
instrucción dentro de un finally
bloque, el destino de la continue
instrucción estará dentro del mismo finally
bloque; de lo contrario, se produce un error en tiempo de compilación.
Se ejecuta una continue
instrucción como se indica a continuación:
- Si la instrucción sale de uno o varios
try
bloques con bloques asociadosfinally
, elcontinue
control se transfiere inicialmente alfinally
bloque de la instrucción mástry
interna. Cuando y si el control llega al punto final de unfinally
bloque, el control se transfiere al bloque de lafinally
siguiente instrucción envolventetry
. Este proceso se repite hasta que se han ejecutado losfinally
bloques de todas las instruccionestry
intermedias. - El control se transfiere al destino de la
continue
instrucción .
Dado que una continue
instrucción transfiere incondicionalmente el control en otro lugar, el punto final de una continue
instrucción nunca es accesible.
13.10.4 La instrucción goto
La goto
instrucción transfiere el control a una instrucción marcada por una etiqueta.
goto_statement
: 'goto' identifier ';'
| 'goto' 'case' constant_expression ';'
| 'goto' 'default' ';'
;
El destino de una goto
instrucción de identificador es la instrucción etiquetada con la etiqueta especificada. Si una etiqueta con el nombre especificado no existe en el miembro de función actual o si la goto
instrucción no está dentro del ámbito de la etiqueta, se produce un error en tiempo de compilación.
Nota: Esta regla permite el uso de una
goto
instrucción para transferir el control fuera de un ámbito anidado, pero no en un ámbito anidado. En el ejemploclass 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}]"); } } }
Se usa una
goto
instrucción para transferir el control fuera de un ámbito anidado.nota final
El destino de una goto case
instrucción es la lista de instrucciones de la instrucción envolvente switch
inmediatamente (§13.8.3), que contiene una case
etiqueta con un patrón constante del valor constante especificado y sin protección. Si la goto case
instrucción no está incluida en una switch
instrucción , si la instrucción envolvente switch
más cercana no contiene este tipo case
o si el constant_expression no es implícitamente convertible (§10.2) al tipo de gobernanza de la instrucción envolvente switch
más cercana, se produce un error en tiempo de compilación.
El destino de una goto default
instrucción es la lista de instrucciones de la instrucción envolvente switch
inmediatamente (§13.8.3), que contiene una default
etiqueta. Si la goto default
instrucción no está incluida en una switch
instrucción o si la instrucción envolvente switch
más cercana no contiene una etiqueta, se produce un default
error en tiempo de compilación.
Una goto
instrucción no puede salir de un finally
bloque (§13.11). Cuando se produce una goto
instrucción dentro de un finally
bloque, el destino de la goto
instrucción estará dentro del mismo finally
bloque o, de lo contrario, se producirá un error en tiempo de compilación.
Se ejecuta una goto
instrucción como se indica a continuación:
- Si la instrucción sale de uno o varios
try
bloques con bloques asociadosfinally
, elgoto
control se transfiere inicialmente alfinally
bloque de la instrucción mástry
interna. Cuando y si el control llega al punto final de unfinally
bloque, el control se transfiere al bloque de lafinally
siguiente instrucción envolventetry
. Este proceso se repite hasta que se han ejecutado losfinally
bloques de todas las instruccionestry
intermedias. - El control se transfiere al destino de la
goto
instrucción .
Dado que una goto
instrucción transfiere incondicionalmente el control en otro lugar, el punto final de una goto
instrucción nunca es accesible.
13.10.5 La instrucción return
La return
instrucción devuelve el control al autor de la llamada actual del miembro de función en el que aparece la instrucción return, devolviendo opcionalmente un valor o un variable_reference (§9.5).
return_statement
: 'return' ';'
| 'return' expression ';'
| 'return' 'ref' variable_reference ';'
;
Un return_statement sin expresión se denomina return-no-value; una expresión contenedora ref
se denomina return-by-ref; y una que contiene solo expresión se denomina return-by-value.
Se trata de un error en tiempo de compilación para usar un valor devuelto sin valor de un método declarado como devuelto por valor o return-by-ref (§15.6.1).
Se trata de un error en tiempo de compilación para usar un valor devuelto por referencia de un método declarado como returns-no-value o returns-by-value.
Se trata de un error en tiempo de compilación para usar un valor devuelto por valor de un método declarado como returns-no-value o returns-by-ref.
Se trata de un error en tiempo de compilación para usar un valor return-by-ref si la expresión no es una variable_reference o es una referencia a una variable cuyo contexto ref-safe-context no es llamador-context (§9.7.2).
Se trata de un error en tiempo de compilación para usar un valor return-by-ref de un método declarado con el method_modifier async
.
Se dice que un miembro de función calcula un valor si es un método con un método devuelto por valor (§15.6.11), un descriptor de acceso get devuelto por valor de una propiedad o indexador, o un operador definido por el usuario. Los miembros de función que son return-no-value no calculan un valor y son métodos con el tipo void
de valor devuelto efectivo , descriptores de acceso set de propiedades e indizadores, agregar y quitar descriptores de acceso de eventos, constructores de instancias, constructores estáticos y finalizadores. Los miembros de función que se devuelven por referencia no calculan un valor.
Para un valor devuelto, una conversión implícita (§10.2) existirá del tipo de expresión al tipo de valor devuelto efectivo (§15.6.11) del miembro de función contenedor. Para una devolución por referencia, una conversión de identidad (§10.2.2) existirá entre el tipo de expresión y el tipo de valor devuelto efectivo del miembro de función contenedor.
return
Las instrucciones también se pueden usar en el cuerpo de expresiones de función anónimas (§12.19) y participar en determinar qué conversiones existen para esas funciones (§10.7.1).
Es un error en tiempo de compilación para que una return
instrucción aparezca en un finally
bloque (§13.11).
Se ejecuta una return
instrucción como se indica a continuación:
- Para un valor devuelto por valor, la expresión se evalúa y su valor se convierte en el tipo de valor devuelto efectivo de la función contenedora mediante una conversión implícita. El resultado de la conversión se convierte en el valor de resultado generado por la función . Para un valor return-by-ref, la expresión se evalúa y el resultado se clasificará como una variable. Si el método de inclusión devuelto por ref incluye
readonly
, la variable resultante es de solo lectura. - Si la instrucción está entre uno o varios bloques o
catch
bloques con bloques asociadosfinally
, elreturn
control se transfiere inicialmente alfinally
bloque de la instrucción mástry
try
interna. Cuando y si el control llega al punto final de unfinally
bloque, el control se transfiere al bloque de lafinally
siguiente instrucción envolventetry
. Este proceso se repite hasta que se han ejecutado los bloques de todas lasfinally
instruccionestry
envolventes. - Si la función contenedora no es una función asincrónica, el control se devuelve al autor de la llamada de la función contenedora junto con el valor de resultado, si existe.
- Si la función contenedora es una función asincrónica, el control se devuelve al autor de la llamada actual y el valor de resultado, si existe, se registra en la tarea de devolución, tal como se describe en (§15.15.3).
Dado que una return
instrucción transfiere incondicionalmente el control en otro lugar, el punto final de una return
instrucción nunca es accesible.
13.10.6 Instrucción throw
La throw
instrucción produce una excepción.
throw_statement
: 'throw' expression? ';'
;
Una throw
instrucción con una expresión produce una excepción generada mediante la evaluación de la expresión. La expresión se convertirá implícitamente en System.Exception
y el resultado de evaluar la expresión se convierte en System.Exception
antes de iniciarse. Si el resultado de la conversión es null
, se produce una System.NullReferenceException
excepción en su lugar.
Una throw
instrucción sin expresión solo se puede usar en un catch
bloque, en cuyo caso, esa instrucción vuelve a producir la excepción que está siendo controlada actualmente por ese catch
bloque.
Dado que una throw
instrucción transfiere incondicionalmente el control en otro lugar, el punto final de una throw
instrucción nunca es accesible.
Cuando se produce una excepción, el control se transfiere a la primera catch
cláusula en una instrucción envolvente try
que puede controlar la excepción. El proceso que tiene lugar desde el punto de la excepción que se inicia hasta el punto de transferir el control a un controlador de excepciones adecuado se conoce como propagación de excepciones. La propagación de una excepción consiste en evaluar repetidamente los pasos siguientes hasta que se encuentre una catch
cláusula que coincida con la excepción. En esta descripción, el punto de inicio es inicialmente la ubicación en la que se produce la excepción. Este comportamiento se especifica en (§21.4).
En el miembro de función actual, se examina cada
try
instrucción que incluye el punto de lanzamiento. Para cada instrucciónS
, empezando por la instrucción mástry
interna y terminando con la instrucción mástry
externa, se evalúan los pasos siguientes:- Si el
try
bloque de incluye el punto deS
inicio y siS
tiene una o variascatch
cláusulas, lascatch
cláusulas se examinan en orden de apariencia para buscar un controlador adecuado para la excepción. La primeracatch
cláusula que especifica un tipoT
de excepción (o un parámetro de tipo que en tiempo de ejecución denota un tipoT
de excepción ), de modo que el tipo de tiempo de ejecución deE
deriva deT
se considera una coincidencia. Si la cláusula contiene un filtro de excepción, el objeto exception se asigna a la variable de excepción y se evalúa el filtro de excepción. Cuando unacatch
cláusula contiene un filtro de excepciones, esacatch
cláusula se considera una coincidencia si el filtro de excepción se evalúa comotrue
. Una cláusula generalcatch
(§13.11) se considera una coincidencia para cualquier tipo de excepción. Si se encuentra una cláusula coincidentecatch
, la propagación de excepciones se completa transfiriendo el control al bloque de esacatch
cláusula. - De lo contrario, si el
try
bloque o uncatch
bloque de incluye el punto deS
inicio y, siS
tiene unfinally
bloque, el control se transfiere alfinally
bloque. Si el bloquefinally
genera otra excepción, finaliza el procesamiento de la excepción actual. De lo contrario, cuando el control alcanza el punto final delfinally
bloque, se continúa el procesamiento de la excepción actual.
- Si el
Si un controlador de excepciones no se encuentra en la invocación de función actual, la invocación de función finaliza y se produce una de las siguientes acciones:
Si la función actual no es asincrónica, los pasos anteriores se repiten para el autor de la llamada de la función con un punto de inicio correspondiente a la instrucción desde la que se invocó el miembro de función.
Si la función actual es asincrónica y devuelve tareas, la excepción se registra en la tarea de devolución, que se coloca en un estado con errores o cancelados, tal y como se describe en §15.15.3.
Si la función actual es asincrónica y
void
-devuelve, se notifica al contexto de sincronización del subproceso actual como se describe en §15.15.4.
Si el procesamiento de excepciones finaliza todas las invocaciones de miembro de función en el subproceso actual, lo que indica que el subproceso no tiene ningún controlador para la excepción, el subproceso finaliza. El impacto de dicha terminación está definido por la implementación.
13.11 Instrucción try
La try
instrucción proporciona un mecanismo para detectar excepciones que se producen durante la ejecución de un bloque. Además, la try
instrucción proporciona la capacidad de especificar un bloque de código que siempre se ejecuta cuando el control deja la try
instrucción .
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
;
Un try_statement consta de la palabra clave try
seguida de un bloque, cero o más catch_clauses y, a continuación, un finally_clause opcional. Habrá al menos un catch_clause o un finally_clause.
En un exception_specifier el tipo, o su clase base efectiva si es un type_parameter, será System.Exception
o un tipo que derive de él.
Cuando una catch
cláusula especifica un class_type y un identificador, se declara una variable de excepción del nombre y el tipo especificados. La variable de excepción se introduce en el espacio de declaración del specific_catch_clause (§7.3). Durante la ejecución del exception_filter y catch
el bloque , la variable de excepción representa la excepción que se está controlando actualmente. Para fines de comprobación de asignaciones definitivas, la variable de excepción se considera definitivamente asignada en todo su ámbito.
A menos que una catch
cláusula incluya un nombre de variable de excepción, es imposible tener acceso al objeto de excepción en el filtro y catch
el bloque.
Una catch
cláusula que especifica ni un tipo de excepción ni un nombre de variable de excepción se denomina cláusula general catch
. Una try
instrucción solo puede tener una cláusula general catch
y, si existe, será la última catch
cláusula.
Nota: Algunos lenguajes de programación pueden admitir excepciones que no se pueden representar como un objeto derivado de
System.Exception
, aunque el código de C# nunca podría generar dichas excepciones. Es posible que se use una cláusula generalcatch
para detectar estas excepciones. Por lo tanto, una cláusula generalcatch
es semánticamente diferente de una que especifica el tipoSystem.Exception
, en que el anterior también podría detectar excepciones de otros lenguajes. nota final
Para localizar un controlador para una excepción, catch
las cláusulas se examinan en orden léxico. Si una catch
cláusula especifica un tipo pero no un filtro de excepciones, se trata de un error en tiempo de compilación para una cláusula posterior catch
de la misma try
instrucción para especificar un tipo que sea el mismo que o se derive de ese tipo.
Nota: Sin esta restricción, sería posible escribir cláusulas inaccesibles
catch
. nota final
Dentro de un catch
bloque, se puede usar una throw
instrucción (§13.10.6) sin ninguna expresión para volver a iniciar la excepción detectada por el catch
bloque. Las asignaciones a una variable de excepción no modifican la excepción que se vuelve a producir.
Ejemplo: en el código siguiente
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); } } }
El método
F
detecta una excepción, escribe información de diagnóstico en la consola, modifica la variable de excepción y vuelve a iniciar la excepción. La excepción que se vuelve a producir es la excepción original, por lo que la salida generada es:Exception in F: G Exception in Main: G
Si el primer
catch
bloque se había producidoe
en lugar de volver a iniciar la excepción actual, la salida generada sería la siguiente:Exception in F: G Exception in Main: F
ejemplo final
Es un error en tiempo de compilación para que una break
instrucción , continue
o goto
transfiera el control fuera de un finally
bloque. Cuando se produce una break
instrucción , continue
o goto
en un finally
bloque, el destino de la instrucción estará dentro del mismo finally
bloque o, de lo contrario, se producirá un error en tiempo de compilación.
Se trata de un error en tiempo de compilación para que se produzca una return
instrucción en un finally
bloque.
Cuando la ejecución alcanza una try
instrucción, el control se transfiere al try
bloque . Si el control alcanza el punto final del try
bloque sin que se propague una excepción, el control se transfiere al finally
bloque si existe uno. Si no existe ningún finally
bloque, el control se transfiere al punto final de la try
instrucción .
Si se ha propagado una excepción, las catch
cláusulas, si las hay, se examinan en orden léxico buscando la primera coincidencia para la excepción. La búsqueda de una cláusula coincidente catch
continúa con todos los bloques envolventes como se describe en §13.10.6. Una catch
cláusula es una coincidencia si el tipo de excepción coincide con cualquier exception_specifier y cualquier exception_filter es true. Una catch
cláusula sin un exception_specifier coincide con ningún tipo de excepción. El tipo de excepción coincide con el exception_specifier cuando el exception_specifier especifica el tipo de excepción o un tipo base del tipo de excepción. Si la cláusula contiene un filtro de excepción, el objeto exception se asigna a la variable de excepción y se evalúa el filtro de excepción.
Si se ha propagado una excepción y se encuentra una cláusula coincidente catch
, el control se transfiere al primer bloque coincidente catch
. Si el control alcanza el punto final del catch
bloque sin que se propague una excepción, el control se transfiere al finally
bloque si existe uno. Si no existe ningún finally
bloque, el control se transfiere al punto final de la try
instrucción . Si se ha propagado una excepción desde el catch
bloque, el control se transfiere al finally
bloque si existe uno. La excepción se propaga a la siguiente instrucción envolvente try
.
Si se ha propagado una excepción y no se encuentra ninguna cláusula coincidente catch
, el control se transfiere al finally
bloque, si existe. La excepción se propaga a la siguiente instrucción envolvente try
.
Las instrucciones de un bloque finally
siempre se ejecutan cuando el control sale de una instrucción try
. Esto es cierto si la transferencia de control se produce como resultado de la ejecución normal, como resultado de ejecutar una break
instrucción , continue
, goto
o return
, o como resultado de propagar una excepción fuera de la try
instrucción . Si el control alcanza el punto final del finally
bloque sin que se propague una excepción, el control se transfiere al punto final de la try
instrucción .
Si se produce una excepción durante la ejecución de un finally
bloque y no se detecta dentro del mismo finally
bloque, la excepción se propaga a la siguiente instrucción envolvente try
. Si otra excepción estaba en proceso de propagación, esa excepción se pierde. El proceso de propagación de una excepción se describe más adelante en la descripción de la throw
instrucción (§13.10.6).
Ejemplo: en el código siguiente
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"); } } }
El método
Method
produce una excepción. La primera acción consiste en examinar las cláusulas envolventescatch
, ejecutando los filtros de excepción. A continuación, lafinally
cláusula de seMethod
ejecuta antes de que el control se transfiera a la cláusula coincidentecatch
envolvente. La salida resultante es:Filter Finally Catch
ejemplo final
El try
bloque de una try
instrucción es accesible si la try
instrucción es accesible.
Se catch
puede acceder a un bloque de una try
instrucción si se puede acceder a la try
instrucción .
El finally
bloque de una try
instrucción es accesible si la try
instrucción es accesible.
El punto final de una try
instrucción es accesible si se cumplen las dos condiciones siguientes:
- El punto final del
try
bloque es accesible o se puede acceder al punto final de al menos uncatch
bloque. - Si hay un
finally
bloque presente, se puede acceder al punto final delfinally
bloque.
13.12 Instrucciones activadas y desactivadas
Las checked
instrucciones y unchecked
se usan para controlar el contexto de comprobación de desbordamiento para operaciones y conversiones aritméticas de tipo entero.
checked_statement
: 'checked' block
;
unchecked_statement
: 'unchecked' block
;
La checked
instrucción hace que todas las expresiones del bloque se evalúen en un contexto comprobado y la unchecked
instrucción hace que todas las expresiones del bloque se evalúen en un contexto sin marcar.
Las checked
instrucciones y unchecked
son exactamente equivalentes a los checked
operadores y unchecked
(§12.8.20), excepto que funcionan en bloques en lugar de expresiones.
13.13 Instrucción lock
La lock
instrucción obtiene el bloqueo de exclusión mutua para un objeto determinado, ejecuta una instrucción y, a continuación, libera el bloqueo.
lock_statement
: 'lock' '(' expression ')' embedded_statement
;
La expresión de una lock
instrucción indicará un valor de un tipo conocido como una referencia. No se realiza ninguna conversión de conversión boxing implícita (§10.2.9) para la expresión de una lock
instrucción y, por tanto, es un error en tiempo de compilación para que la expresión denota un valor de un value_type.
Instrucción lock
del formulario
lock (x)
…
donde x
es una expresión de un reference_type, es exactamente equivalente a:
bool __lockWasTaken = false;
try
{
System.Threading.Monitor.Enter(x, ref __lockWasTaken);
...
}
finally
{
if (__lockWasTaken)
{
System.Threading.Monitor.Exit(x);
}
}
salvo que x
solo se evalúa una vez.
Aunque se mantiene un bloqueo de exclusión mutua, el código que se ejecuta en el mismo subproceso de ejecución también puede obtener y liberar el bloqueo. Sin embargo, se impide que el código que se ejecute en otros subprocesos obtenga el bloqueo hasta que se libere el bloqueo.
13.14 Instrucción using
La using
instrucción obtiene uno o varios recursos, ejecuta una instrucción y, a continuación, elimina el recurso.
using_statement
: 'using' '(' resource_acquisition ')' embedded_statement
;
resource_acquisition
: local_variable_declaration
| expression
;
Un recurso es una clase o estructura que implementa la System.IDisposable
interfaz, que incluye un único método sin parámetros denominado Dispose
. El código que usa un recurso puede llamar Dispose
a para indicar que el recurso ya no es necesario.
Si la forma de resource_acquisition es local_variable_declaration, el tipo del local_variable_declaration será o dynamic
un tipo que se pueda convertir implícitamente en System.IDisposable
. Si la forma de resource_acquisition es expresión , esta expresión se podrá convertir implícitamente en System.IDisposable
.
Las variables locales declaradas en un resource_acquisition son de solo lectura e incluirán un inicializador. Se produce un error en tiempo de compilación si la instrucción insertada intenta modificar estas variables locales (a través de la asignación o los ++
operadores y --
), tomar la dirección de ellas o pasarlas como parámetros de referencia o salida.
Una using
instrucción se traduce en tres partes: adquisición, uso y eliminación. El uso del recurso se incluye implícitamente en una try
instrucción que incluye una finally
cláusula . Esta finally
cláusula elimina el recurso. Si se adquiere un null
recurso, no se realiza ninguna llamada a Dispose
y no se produce ninguna excepción. Si el recurso es de tipo dynamic
, se convierte dinámicamente a través de una conversión dinámica implícita (§10.2.10) a IDisposable
durante la adquisición para asegurarse de que la conversión se realiza correctamente antes del uso y eliminación.
Instrucción using
del formulario
using (ResourceType resource = «expression» ) «statement»
corresponde a una de las tres expansiones posibles. Cuando ResourceType
es un tipo de valor que no acepta valores NULL o un parámetro de tipo con la restricción de tipo de valor (§15.2.5), la expansión es semánticamente equivalente a
{
ResourceType resource = «expression»;
try
{
«statement»;
}
finally
{
((IDisposable)resource).Dispose();
}
}
salvo que la conversión de resource
en System.IDisposable
no hará que se produzca la conversión boxing.
De lo contrario, cuando ResourceType
es dynamic
, la expansión es
{
ResourceType resource = «expression»;
IDisposable d = resource;
try
{
«statement»;
}
finally
{
if (d != null)
{
d.Dispose();
}
}
}
De lo contrario, la expansión es
{
ResourceType resource = «expression»;
try
{
«statement»;
}
finally
{
IDisposable d = (IDisposable)resource;
if (d != null)
{
d.Dispose();
}
}
}
En cualquier expansión, la resource
variable es de solo lectura en la instrucción insertada, y la d
variable es inaccesible en la instrucción insertada y es invisible para ella.
Se permite que una implementación implemente una using_statement determinada de forma diferente, por ejemplo, por motivos de rendimiento, siempre que el comportamiento sea coherente con la expansión anterior.
Una using
instrucción del formulario:
using («expression») «statement»
tiene las mismas tres expansiones posibles. En este caso ResourceType
, es implícitamente el tipo en tiempo de compilación de la expresión, si tiene uno. De lo contrario, la propia interfaz IDisposable
se usa como .ResourceType
La resource
variable no es accesible en la instrucción insertada y es invisible.
Cuando un resource_acquisition adopta la forma de un local_variable_declaration, es posible adquirir varios recursos de un tipo determinado. Instrucción using
del formulario
using (ResourceType r1 = e1, r2 = e2, ..., rN = eN) «statement»
es exactamente equivalente a una secuencia de instrucciones anidadas using
:
using (ResourceType r1 = e1)
using (ResourceType r2 = e2)
...
using (ResourceType rN = eN)
«statement»
Ejemplo: En el ejemplo siguiente se crea un archivo denominado log.txt y se escriben dos líneas de texto en el archivo. A continuación, se abre ese mismo archivo para leer y copiar las líneas de texto contenidas en la consola.
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); } } } }
Dado que las
TextWriter
clases yTextReader
implementan laIDisposable
interfaz , el ejemplo puede usarusing
instrucciones para asegurarse de que el archivo subyacente está cerrado correctamente después de las operaciones de escritura o lectura.ejemplo final
13.15 Instrucción yield
La yield
instrucción se usa en un bloque de iterador (§13.3) para producir un valor para el objeto enumerador (§15.14.5) o un objeto enumerable (§15.14.6) de un iterador o para indicar el final de la iteración.
yield_statement
: 'yield' 'return' expression ';'
| 'yield' 'break' ';'
;
yield
es una palabra clave contextual (§6.4.4) y tiene un significado especial solo cuando se usa inmediatamente antes de una return
palabra clave o break
.
Hay varias restricciones sobre dónde puede aparecer una yield
instrucción, como se describe en lo siguiente.
- Es un error en tiempo de compilación para que una
yield
instrucción (de cualquiera de las formas) aparezca fuera de un method_body, operator_body o accessor_body. - Es un error en tiempo de compilación para que una
yield
instrucción (de cualquiera de las formas) aparezca dentro de una función anónima. - Es un error en tiempo de compilación para que una
yield
instrucción (de cualquiera de las formas) aparezca en lafinally
cláusula de unatry
instrucción . - Es un error en tiempo de compilación para que una
yield return
instrucción aparezca en cualquier parte de unatry
instrucción que contenga cualquier catch_clauses.
Ejemplo: en el ejemplo siguiente se muestran algunos usos válidos e no válidos de
yield
instrucciones.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 }
ejemplo final
Una conversión implícita (§10.2) existirá del tipo de expresión de la yield return
instrucción al tipo de rendimiento (§15.14.4) del iterador.
Se ejecuta una yield return
instrucción como se indica a continuación:
- La expresión dada en la instrucción se evalúa, se convierte implícitamente en el tipo de rendimiento y se asigna a la
Current
propiedad del objeto enumerador. - Se suspende la ejecución del bloque de iterador. Si la
yield return
instrucción está dentro de uno o variostry
bloques, los bloques asociadosfinally
no se ejecutan en este momento. - El
MoveNext
método del objeto enumerador vuelvetrue
a su llamador, lo que indica que el objeto enumerador ha avanzado correctamente al siguiente elemento.
La siguiente llamada al método del MoveNext
objeto enumerador reanuda la ejecución del bloque iterador desde donde se suspendió por última vez.
Se ejecuta una yield break
instrucción como se indica a continuación:
- Si la instrucción está entre uno o varios
try
bloques con bloques asociadosfinally
, elyield break
control se transfiere inicialmente alfinally
bloque de la instrucción mástry
interna. Cuando y si el control llega al punto final de unfinally
bloque, el control se transfiere al bloque de lafinally
siguiente instrucción envolventetry
. Este proceso se repite hasta que se han ejecutado los bloques de todas lasfinally
instruccionestry
envolventes. - El control se devuelve al autor de la llamada del bloque de iterador. Este es el método o
Dispose
elMoveNext
método del objeto enumerador.
Dado que una yield break
instrucción transfiere incondicionalmente el control en otro lugar, el punto final de una yield break
instrucción nunca es accesible.
ECMA C# draft specification