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;enfinallybloque? ¿no es lo mismo quecontinue;después deltry -- catchbloqueo?finally/continuees una limitación del compilador de C # :-) También tengo curiosidad sobre la razón de esta limitación.brfamilia de instrucciones de rama no puede lograr esto.Respuestas:
finallylos 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,
finallyse 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
finallyno tiene sentido permitir transferir el control desde el interior de unfinallybloque 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
continuesolo cuando no se produzcan excepciones no detectadas, simplemente colóquelocontinuefuera del bloque try.fuente
continueen unfinallybloque estará bien si solo continúa un bucle local definido dentro delfinallybloque. En la pregunta, sin embargo, intenta continuar un ciclo "externo". Declaraciones similares que no puede tener dentro de unfinallybloque sonreturn,break(al salir del bloque) ygoto(al ir a una etiqueta fuera delfinallybloque). 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
breakocontinue. 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
finallydeclaración se vería afectada por si secatchhabí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
continuede 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 oreturnbloques. 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 sucontinuese usa solo en casos donde el flujo de control no se está transfiriendo.fuente
En general
continue, no tiene sentido cuando se usa enfinallybloque. Mira esto:fuente
continueno tiene sentido fuera de la declaración de ciclo, pero no significa que no sea compatible.itempuede ser archivo, lectura fallida ->finallycierra el archivo.continueevita 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? Tucontinuequieres ir en una dirección, la excepción manejando otra.fuente
finallycuando lo tengascatch. Se usa comúnmente para cerrar recursos de manera confiable.returndeclaración dentro de sus bloquestryocatch. ¿Que hacemos ahora? ¿Volver o continuar el bucle? (y si continuamos, ¿qué pasa si es el último elemento en iterar? ¿Entonces simplemente continuamos fuera delforeachy el retorno nunca ocurre?) EDITAR: O inclusogotoa una etiqueta fuera del ciclo, prácticamente cualquier acción que transfiera el control fuera delforeachciclo. .returndentro 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
finallybloque se puede ejecutar con una excepción a la espera de ser relanzado. Realmente no tendría sentido poder salir del bloque (mediante acontinueo 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
finallyse ejecuta tanto si se lanza una excepción no detectada como si no. Otros ya han explicado por qué esto lo vuelvecontinueiló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
continueal final de cada unocatch, y (2) podría satisfacerse almacenando excepciones no detectadas para lanzarlas más tarde. Podrías escribirlo así:En realidad,
finallytambié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 solocontinuedespué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
brinstrucció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
finallybloque 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
finallybloque 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
finallypor 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. Sifinallyllama 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.finallyes 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.finallyno. 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 decontinueetc., 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").finallybloque puede violarla. Realmente una situación desagradable, que en mi humilde opinión debería haber sido resuelta al mínimo informando a losfinallybloques sobre cualquier excepción pendiente para que puedan construir objetos de excepción compuestos.