No tengo ningún problema; Tengo curiosidad. Imagina el siguiente escenario:
foreach (var foo in list)
{
try
{
//Some code
}
catch (Exception)
{
//Some more code
}
finally
{
continue;
}
}
Esto no se compilará, ya que genera el error del compilador CS0157 :
El control no puede salir del cuerpo de una cláusula final
¿Por qué?
continue;
enfinally
bloque? ¿no es lo mismo quecontinue;
después deltry -- catch
bloqueo?finally/continue
es una limitación del compilador de C # :-) También tengo curiosidad sobre la razón de esta limitación.br
familia de instrucciones de rama no puede lograr esto.Respuestas:
finally
los bloques se ejecutan tanto si se lanza una excepción como si no. Si se lanza una excepción, ¿qué diablos haríacontinue
? 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 ninguna excepción,
finally
se ejecutará cuando se ejecuten otras sentencias de transferencia de control dentro del bloque try / catch, comoreturn
, por ejemplo, que trae el mismo problema.En resumen, con la semántica de
finally
no tiene sentido permitir transferir el control desde el interior de unfinally
bloque al exterior de este.Apoyar esto con alguna semántica alternativa sería más confuso que útil, ya que existen soluciones simples que hacen que el comportamiento deseado sea más claro. Entonces, obtiene un error y se ve obligado a pensar correctamente sobre su problema. Es la idea general de "arrojarte al pozo del éxito" que sigue 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:
Si desea hacerlo
continue
solo cuando no se produzcan excepciones no detectadas, simplemente colóquelocontinue
fuera del bloque try.fuente
continue
en unfinally
bloque estará bien si solo continúa un bucle local definido dentro delfinally
bloque. En la pregunta, sin embargo, intenta continuar un ciclo "externo". Declaraciones similares que no puede tener dentro de unfinally
bloque sonreturn
,break
(al salir del bloque) ygoto
(al ir a una etiqueta fuera delfinally
bloque). Para una discusión de Java relacionada, consulte Regresar de un bloque finalmente en Java .Aquí hay una fuente confiable:
Se ha tomado de MSDN, 8.9.2 La declaración continue .
La documentación dice que:
Es de aquí 8.10 La declaración de prueba .
fuente
Puede pensar que tiene sentido, pero en realidad no tiene sentido .
¿Qué piensa hacer un descanso o continuar cuando se lanza una excepción? El equipo del compilador de C # no quiere tomar decisiones por su cuenta asumiendo
break
ocontinue
. En cambio, decidieron quejarse de que la situación del desarrollador será ambigua para transferir el controlfinally block
.Por lo tanto, el trabajo del desarrollador es indicar claramente lo que pretende hacer en lugar de que el compilador asuma otra cosa.
¡Espero que entiendas por qué esto no se compila!
fuente
finally
declaración se vería afectada por si secatch
había salido a través de una excepción, y no hay ningún mecanismo para decir cómo debería interactuar con acontinue
. Sin embargo, me gusta tu ejemplo porque muestra un problema aún mayor.Como han dicho otros, pero centrados en las excepciones, en realidad se trata de un manejo ambiguo de la transferencia de control.
En su mente, probablemente esté pensando en un escenario como este:
Teóricamente, 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
El temido Goto
El regreso
El rompimiento
Así que en conclusión, sí, mientras que no es una ligera posibilidad de utilizar una
continue
de las situaciones en que no se están transfiriendo el control, pero una buena oferta (la mayoría?) De los casos se trata de excepciones oreturn
bloques. Los diseñadores del lenguaje sintieron que esto sería demasiado ambiguo y (probablemente) imposible de garantizar en el momento de la compilación que sucontinue
se usa solo en casos donde el flujo de control no se está transfiriendo.fuente
En general
continue
, no tiene sentido cuando se usa enfinally
bloque. Mira esto:fuente
continue
no tiene sentido fuera de la declaración de ciclo, pero no significa que no sea compatible.item
puede ser archivo, lectura fallida ->finally
cierra el archivo.continue
evita el resto del procesamiento.Bueno, creo que no.
Cuando literalmente lo
catch(Exception)
tienes, no necesitas el finalmente (y probablemente ni siquiera elcontinue
).Cuando tenga el más realista
catch(SomeException)
, ¿qué debería suceder cuando no se detecta una excepción? Tucontinue
quieres ir en una dirección, la excepción manejando otra.fuente
finally
cuando lo tengascatch
. Se usa comúnmente para cerrar recursos de manera confiable.return
declaración dentro de sus bloquestry
ocatch
. ¿Que hacemos ahora? ¿Volver o continuar el bucle? (y si continuamos, ¿qué pasa si es el último elemento en iterar? ¿Entonces simplemente continuamos fuera delforeach
y el retorno nunca ocurre?) EDITAR: O inclusogoto
a una etiqueta fuera del ciclo, prácticamente cualquier acción que transfiera el control fuera delforeach
ciclo. .return
dentro del bloque. Pero normalmente la ejecución continúa después del catch-all.No se puede dejar el cuerpo de un bloque definitivo. Esto incluye palabras clave de descanso, retorno y, en su caso, continuar.
fuente
El
finally
bloque se puede ejecutar con una excepción a la espera de ser relanzado. Realmente no tendría sentido poder salir del bloque (mediante acontinue
o cualquier otra cosa) sin volver a lanzar la excepción.Si desea continuar con su ciclo pase lo que pase, no necesita la declaración finalmente: simplemente capture la excepción y no vuelva a lanzar.
fuente
finally
se ejecuta tanto si se lanza una excepción no detectada como si no. Otros ya han explicado por qué esto lo vuelvecontinue
ilógico, pero aquí hay una alternativa que sigue el espíritu de lo que parece estar pidiendo este código. Básicamente,finally { continue; }
está diciendo:(1) podría satisfacerse colocando
continue
al final de cada unocatch
, y (2) podría satisfacerse almacenando excepciones no detectadas para lanzarlas más tarde. Podrías escribirlo así:En realidad,
finally
también se habría ejecutado en el tercer caso, donde no hubo excepciones lanzadas en absoluto, capturadas o no capturadas. Si lo desea, por supuesto, puede colocar un solocontinue
después del bloque try-catch en lugar de dentro de cada unocatch
.fuente
Técnicamente hablando, es una limitación del CIL subyacente. De la especificación del idioma :
y
En la página del documento para la
br
instrucción :Este último es válido para todas las instrucciones de bifurcación, incluyendo
beq
,brfalse
, etc.fuente
Los diseñadores del lenguaje simplemente no querían (o no podían) razonar acerca de que la semántica de un bloque final fuera terminado por una transferencia de control.
Un problema, o quizás el problema clave, es que el
finally
bloque se ejecuta como parte de alguna transferencia de control no local (procesamiento de excepción). El objetivo de esa transferencia de control no es el ciclo circundante; el procesamiento de excepciones aborta el ciclo y continúa desenrollando más.Si tenemos una transferencia de control fuera del
finally
bloque de limpieza, entonces la transferencia de control original está siendo "secuestrada". Se cancela y el control va a otra parte.La semántica se puede resolver. Otros idiomas lo tienen.
Los diseñadores de C # decidieron simplemente no permitir las transferencias de control estáticas, "tipo goto", simplificando un poco las cosas.
Sin embargo, incluso si hace eso, no resuelve la pregunta de qué sucede si se inicia una transferencia dinámica desde un
finally
: ¿y si el bloque finalmente llama a una función y esa función lanza? El procesamiento de la excepción original se "secuestra".Si trabaja con la semántica de esta segunda forma de secuestro, no hay razón para desterrar el primer tipo. En realidad, son lo mismo: una transferencia de control es una transferencia de control, tenga el mismo alcance léxico o no.
fuente
finally
por definición, debe seguir inmediatamente la transferencia que la desencadenó (una excepción no detectadareturn
, etc.). Sería fácil permitir transferencias "goto-like" dentrofinally
(¿qué lenguajes hacen esto, por cierto?), Pero hacerlo significaría que estry { return } finally { ... }
posible que no regrese , lo cual es completamente inesperado. Sifinally
llama a una función que lanza, eso no es realmente lo mismo, porque ya esperamos que puedan ocurrir excepciones en cualquier momento e interrumpir el flujo normal.finally
es algo parecido, ya que puede resultar en un flujo de programa inesperado e ilógico. La única diferencia es que los diseñadores del lenguaje, al no poder rechazar todos los programas en los que un bloque finalmente podría lanzar una excepción no controlada, en cambio permiten que dichos programas se compilen y esperan que el programa pueda aceptar cualquier consecuencia que pueda seguir al abandono de la anterior excepción: desenrollar secuencia.finally
no. Según la lógica del último párrafo de Kaz, las funciones también deberían tener la libertad de afectar el flujo en el alcance de su llamador a través decontinue
etc., porque se les permite hacerlo mediante excepciones. Pero esto, por supuesto, no está permitido; las excepciones son la única excepción a esta regla, de ahí el nombre "excepción" (o "interrupción").finally
bloque puede violarla. Realmente una situación desagradable, que en mi humilde opinión debería haber sido resuelta al mínimo informando a losfinally
bloques sobre cualquier excepción pendiente para que puedan construir objetos de excepción compuestos.