usos uso que programacion operando interfaz implementar debe como c# .net

c# - uso - que es using en programacion



¿Por qué una instrucción ''continuar'' no puede estar dentro de un bloque ''finalmente''? (11)

No tengo un problema; Tengo curiosidad. Imagine el siguiente escenario:

foreach (var foo in list) { try { //Some code } catch (Exception) { //Some more code } finally { continue; } }

Esto no se compilará, ya que aumenta el error de compilación CS0157 :

El control no puede abandonar el cuerpo de una cláusula final

¿Por qué?


"Esto no compilará y creo que tiene mucho sentido"

Bueno, creo que no.

Cuando literalmente tienes catch(Exception) entonces no necesitas el finally (y probablemente ni siquiera el continue ).

Cuando tiene la catch(SomeException) más realista catch(SomeException) , ¿qué debería pasar cuando no se catch(SomeException) una excepción? Tu continue quiere ir de una manera, la excepción manejando otra.


Aquí hay una fuente confiable:

Una instrucción continue no puede salir de un bloque finally (Sección 8.10). Cuando una instrucción continue ocurre dentro de un bloque finally, el objetivo de la instrucción continue debe estar dentro del mismo bloque finally; de lo contrario, se produce un error en tiempo de compilación.

Se toma de MSDN, 8.9.2 La declaración de continuación .

La documentación dice que:

Las instrucciones de un bloque finally siempre se ejecutan cuando el control deja una instrucción try. Esto es así ya sea que la transferencia de control ocurra como resultado de la ejecución normal, como resultado de la ejecución de una sentencia break, continue, goto o return, o como resultado de la propagación de una excepción fuera de la instrucción try. Si se lanza una excepción durante la ejecución de un bloque finally, la excepción se propaga a la siguiente instrucción try que lo encierra. 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 analiza con más detalle en la descripción de la declaración throw (Sección 8.9.5).

Es de aquí 8.10 La declaración try .


Como han dicho otros, pero centrado en las excepciones, se trata realmente de un manejo ambiguo del control de transferencia.

En su mente, probablemente esté pensando en un escenario como este:

public static object SafeMethod() { foreach(var item in list) { try { try { //do something that won''t transfer control outside } catch { //catch everything to not throw exceptions } } finally { if (someCondition) //no exception will be thrown, //so theoretically this could work continue; } } return someValue; }

En teoría, puede rastrear el flujo de control y decir, sí, esto está "bien". No se lanza ninguna excepción, no se transfiere ningún control. Pero los diseñadores del lenguaje C # tenían otros problemas en mente.

La excepción lanzada

public static void Exception() { try { foreach(var item in list) { try { throw new Exception("What now?"); } finally { continue; } } } catch { //do I get hit? } }

El temido Goto

public static void Goto() { foreach(var item in list) { try { goto pigsfly; } finally { continue; } } pigsfly: }

El regreso

public static object ReturnSomething() { foreach(var item in list) { try { return item; } finally { continue; } } }

El rompimiento

public static void Break() { foreach(var item in list) { try { break; } finally { continue; } } }

Entonces, en conclusión, sí, mientras que existe una pequeña posibilidad de utilizar un continue en situaciones donde el control no se transfiere, pero un buen trato (¿la mayoría?) De los casos involucran excepciones o bloqueos de return . Los diseñadores de idiomas consideraron que esto sería demasiado ambiguo y (probablemente) imposible de asegurar en el momento de la compilación que su continue se use solo en casos donde el flujo de control no se transfiere.


El bloque finally se puede ejecutar con una excepción en espera de volver a lanzarse. Realmente no tendría sentido poder salir del bloque (por un continue o cualquier otra cosa) sin volver a lanzar la excepción.

Si desea continuar su ciclo pase lo que pase, no necesita la declaración final: solo atrape la excepción y no vuelva a lanzar.


En general, continue no tiene sentido cuando se usa en el bloque finally . Mira esto:

foreach (var item in list) { try { throw new Exception(); } finally{ //doesn''t make sense as we are after exception continue; } }


Los diseñadores del lenguaje simplemente no querían (o no podían) razonar sobre la semántica de un bloque finally que terminaba con una transferencia de control.

Una cuestión, o tal vez la cuestión clave, es que el bloque finally se ejecuta como parte de una transferencia de control no local (procesamiento de excepción). El objetivo de esa transferencia de control no es el bucle adjunto; el procesamiento de la excepción anula el ciclo y continúa avanzando.

Si tenemos una transferencia de control fuera del bloque de limpieza final, entonces la transferencia de control original está siendo "secuestrada". Se cancela y el control se va a otro lado.

La semántica puede ser resuelta. Otros idiomas lo tienen.

Los diseñadores de C # decidieron simplemente no permitir las transferencias de control estáticas, "goto-like", lo que simplifica un poco las cosas.

Sin embargo, incluso si lo hace, no resuelve la cuestión de qué sucede si se inicia una transferencia dinámica desde un finally : ¿qué ocurre si el bloque finally llama a una función y esa función arroja errores? El procesamiento original de la excepción se "secuestra".

Si resuelve la semántica de esta segunda forma de secuestro, no hay ninguna razón para desterrar el primer tipo. Son realmente lo mismo: una transferencia de control es una transferencia de control, ya sea con el mismo alcance léxico o no.


No puedes abandonar el cuerpo de un bloque finalmente. Esto incluye las palabras clave break, return y en su caso continuar.


Puede pensar que tiene sentido, pero en realidad no tiene sentido .

foreach (var v in List) { try { //Some code } catch (Exception) { //Some more code break; or return; } finally { continue; } }

¿Qué piensas hacer un descanso o continuar cuando se lanza una excepción? El equipo del compilador de C # no quiere tomar la decisión por sí mismo al asumir el break o continue . En cambio, decidieron quejarse de que la situación del desarrollador será ambigua para transferir el control del finally block .

Por lo tanto, es tarea del desarrollador indicar claramente qué intenta hacer en lugar de compilar asumiendo otra cosa.

¡Espero que entiendas por qué esto no compila!


Técnicamente hablando, es una limitación del CIL subyacente. Desde la especificación del idioma :

No se permite nunca que la transferencia de control entre en un controlador de captura ni en una cláusula final, excepto a través del mecanismo de manejo de excepciones.

y

La transferencia de control fuera de una región protegida solo está permitida mediante una instrucción de excepción ( leave , end.filter , end.catch o end.finally )

En la página de documentación para la instrucción br :

Esta instrucción no puede realizar transferencias de control dentro y fuera de los bloques try, catch, filter y finally.

Esto último es cierto para todas las instrucciones de bifurcación, incluidas beq , brfalse , etc.


finally bloques se ejecutan si se lanza una excepción o no. Si se lanza una excepción, ¿qué diablos continue ? No puede continuar la ejecución del bucle, porque una excepción no detectada transferirá el control a otra función.

Incluso si no se lanza una excepción, finally se ejecutará cuando se ejecuten otras sentencias de transferencia de control dentro del bloque try / catch, como una return , por ejemplo, que trae el mismo problema.

En resumen, con la semántica de, finally , no tiene sentido permitir la transferencia de control desde el interior de un bloque final al exterior.

Respaldar esto con una semántica alternativa sería más confuso que útil, ya que existen soluciones simples que hacen que el comportamiento previsto sea mucho más claro. Entonces, se obtiene un error y se ven obligados a pensar correctamente sobre su problema. Es la idea general de "tirar al pozo del éxito" que sucede en C #.

Si desea ignorar las excepciones (la mayoría de las veces es una mala idea) y continuar ejecutando el ciclo, use un bloque catch all:

foreach ( var in list ) { try{ //some code }catch{ continue; } }

Si desea continue solo cuando no se lanzan excepciones no detectadas, simplemente continue fuera del bloque try.


finally ejecuta si se lanza una excepción no detectada. Otros ya han explicado por qué esto continue ilógico, pero aquí hay una alternativa que sigue el espíritu de lo que este código parece estar pidiendo. Básicamente, finally { continue; } finally { continue; } está diciendo:

  1. Cuando hay excepciones detectadas, continúe
  2. Cuando haya excepciones no detectadas, permita que se arrojen, pero continúen

(1) podría cumplirse colocando continue al final de cada catch , y (2) podría cumplirse almacenando excepciones no detectadas para lanzarlas más tarde. Podrías escribirlo así:

var exceptions = new List<Exception>(); foreach (var foo in list) { try { // some code } catch (InvalidOperationException ex) { // handle specific exception continue; } catch (Exception ex) { exceptions.Add(ex); continue; } // some more code } if (exceptions.Any()) { throw new AggregateException(exceptions); }

En realidad, finally también se habría ejecutado en el tercer caso, donde no se lanzaron excepciones, atrapadas o no. Si así lo deseaba, podría, por supuesto, colocar un solo continue después del bloque try-catch en lugar de dentro de cada catch .