Lo siguiente está bien:
try
{
Console.WriteLine("Before");
yield return 1;
Console.WriteLine("After");
}
finally
{
Console.WriteLine("Done");
}
El finally
bloque se ejecuta cuando todo ha terminado de ejecutarse ( IEnumerator<T>
admite IDisposable
proporcionar una forma de garantizar esto incluso cuando la enumeración se abandona antes de que finalice).
Pero esto no está bien:
try
{
Console.WriteLine("Before");
yield return 1; // error CS1626: Cannot yield a value in the body of a try block with a catch clause
Console.WriteLine("After");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Supongamos (por el bien del argumento) que una u otra de las WriteLine
llamadas dentro del bloque try lanza una excepción . ¿Cuál es el problema de continuar la ejecución en catch
bloque?
Por supuesto, la parte de retorno de rendimiento (actualmente) no puede arrojar nada, pero ¿por qué eso debería evitar que tengamos un cierre try
/ catch
para lidiar con las excepciones lanzadas antes o después de un yield return
?
Actualización: Hay un comentario interesante de Eric Lippert aquí : ¡parece que ya tienen suficientes problemas para implementar correctamente el comportamiento de prueba / finalmente!
EDITAR: La página de MSDN en este error es: http://msdn.microsoft.com/en-us/library/cs1x15az.aspx . Sin embargo, no explica por qué.
fuente
Respuestas:
Sospecho que se trata de una cuestión de practicidad más que de viabilidad. Sospecho que hay muy, muy pocas ocasiones en las que esta restricción es en realidad un problema que no se puede solucionar, pero la complejidad adicional en el compilador sería muy significativa.
Hay algunas cosas como esta que ya he encontrado:
En cada uno de estos casos, sería posible ganar un poco más de libertad, a costa de una complejidad adicional en el compilador. El equipo tomó la decisión pragmática, por lo que los aplaudo: prefiero tener un lenguaje un poco más restrictivo con un compilador 99.9% preciso (sí, hay errores; me encontré con uno en SO el otro día) que uno más lenguaje flexible que no se pudo compilar correctamente.
EDITAR: Aquí hay una pseudoprueba de cómo es factible.
Considere eso:
Ahora transforma:
en (especie de pseudocódigo):
La única duplicación está en la configuración de bloques try / catch, pero eso es algo que el compilador ciertamente puede hacer.
Es posible que me haya perdido algo aquí; si es así, ¡házmelo saber!
fuente
using
yforeach
. Por ejemplo:try{foreach (string s in c){yield return s;}}catch(Exception){}
yield
, en mi opinión, debido al código espagueti que tienes que escribir para solucionarlo.yield
OMI, está muy lejos de ser grave .Todas las
yield
declaraciones en una definición de iterador se convierten a un estado en una máquina de estado que utiliza efectivamente unaswitch
declaración para avanzar estados. Si se hizo generar código parayield
declaraciones en un try / catch tendría que duplicar todo en eltry
bloque para cadayield
declaración, excluyendo todos los demásyield
declaración para ese bloque. Esto no siempre es posible, especialmente si unayield
declaración depende de una anterior.fuente
Yo especularía que debido a la forma en que la pila de llamadas se enrolla / desenrolla cuando se obtiene el retorno de un enumerador, se vuelve imposible que un bloque try / catch "capture" la excepción. (porque el bloque de retorno de rendimiento no está en la pila, aunque él originó el bloque de iteración)
Para tener una idea de lo que estoy hablando, configure un bloque de iterador y un foreach usando ese iterador. Verifique cómo se ve la pila de llamadas dentro del bloque foreach y luego verifíquelo dentro del bloque try / finalmente del iterador.
fuente
He aceptado la respuesta de THE INVINCIBLE SKEET hasta que alguien de Microsoft viene a echar agua fría sobre la idea. Pero no estoy de acuerdo con la parte de la cuestión de la opinión; por supuesto, un compilador correcto es más importante que uno completo, pero el compilador de C # ya es muy inteligente al resolver esta transformación para nosotros en la medida en que lo hace. Un poco más de integridad en este caso haría que el lenguaje sea más fácil de usar, enseñar, explicar, con menos casos extremos o errores. Así que creo que valdría la pena el esfuerzo extra. Algunos tipos en Redmond se rascan la cabeza durante quince días y, como resultado, millones de programadores durante la próxima década pueden relajarse un poco más.
(También albergo un sórdido deseo de que haya una manera de hacer
yield return
lanzar una excepción que ha sido metida en la máquina de estado "desde el exterior", por el código que impulsa la iteración. Pero mis razones para querer esto son bastante oscuras).En realidad, una consulta que tengo sobre la respuesta de Jon tiene que ver con el lanzamiento de la expresión de retorno de rendimiento.
Obviamente, el rendimiento de rendimiento 10 no es tan malo. Pero esto sería malo:
Entonces, ¿no tendría más sentido evaluar esto dentro del bloque try / catch anterior?
El siguiente problema serían los bloques try / catch anidados y las excepciones relanzadas:
Pero estoy seguro de que es posible ...
fuente