Ésta es una muy interesante pregunta.
Recuerde que en Linq, se proporcionan muchos operadores estándar que encadenan eficazmente. Actualmente no hay uno que le permita ajustar el manejo de excepciones personalizado alrededor de una secuencia interna.
Así que mi sugerencia es escribir una nueva que le permite especificar una acción que se ocupa de cualquier excepción que se produce durante la ejecución de IEnumerator.MoveNext
:
public static class EnumerableExceptions
{
public static IEnumerable<TItem> Catch<TItem, TEx>(
this IEnumerable<TItem> source,
Action<TEx> handler) where TEx : Exception
{
using (var enumerator = source.GetEnumerator())
{
for (; ;)
{
try
{
if (!enumerator.MoveNext())
yield break;
}
catch (TEx x)
{
handler(x);
yield break;
}
yield return enumerator.Current;
}
}
}
}
Así que ahora suponiendo que teníamos esto:
public class NastyException : Exception { }
public static IEnumerable<String> StringYielder()
{
yield return "apple";
yield return "banana";
throw new NastyException();
yield return "oracle";
yield return "grapefruit";
yield return "microsoft";
}
Nos gustaría poder envolver todo el cuerpo en un try
/catch
, lo cual es lamentablemente ilegal. Pero lo que podemos hacer es envolver la secuencia generada:
public static IEnumerable<String> LoggingStringYielder()
{
return StringYielder().Catch(
(NastyException ex) =>
Console.WriteLine("Exception caught: " + ex.StackTrace));
}
Eso es, me sale una secuencia llamando a la "cruda" StringYielder
método, y luego aplicar el nuevo operador Catch
a la misma, especificando qué hacer si se produce un cierto tipo de excepción. Aquí solo voy a imprimir el seguimiento de la pila.
Así que si hago esto:
foreach (var str in LoggingStringYielder())
Console.WriteLine(str);
El programa se completa sin que se caiga, y la salida es:
apple
banana
Exception caught: at ConsoleApplication7.Program.<StringYielder>.. blah
Así que, aunque no se puede poner un intento de captura en todo el código dentro de la método iterador original, ahora puede "envolverlo" alrededor de ese método iterador. Es como una forma no intrusiva de inyectar el manejo de excepciones alrededor del código entre cada yield return
.
¡Actualización de bonificación!
a ser muy exigente con la forma en que redactado esta última frase:
En primer lugar se puede tirar antes de la primera yield return
y es tratado de la misma manera, como el código se ejecuta en la primera llamada a MoveNext
. Entonces, "... el código antes de cada ..." hubiera sido más preciso que "... el código entre cada uno ...".
En segundo lugar, un yield return
puede aceptar una expresión que debe evaluarse y que puede arrojarse durante la evaluación. Eso debe considerarse como código que se ejecuta antes de que ocurra el yield return
, aunque sintácticamente aparece después de él.
Ok, actualicé la pregunta para reflejar que quiero obtener la excepción _if_ se arrojó. Aún así, no se permite capturar alrededor del rendimiento ... – KristoferA