2009-08-18 11 views

Respuesta

34
myEnumerable.Select(a => 
    { 
    try 
    { 
     return ThisMethodMayThrowExceptions(a)); 
    } 
    catch(Exception) 
    { 
     return defaultValue; 
    } 
    }); 

Pero en realidad tiene olor.

Sobre la sintaxis lambda:

x => x.something 

es una especie de acceso directo y se podría escribir como

(x) => { return x.something; } 
18

llamada una proyección que tiene que tratar/catch:

myEnumerable.Select(a => TryThisMethod(a)); 

... 

public static Bar TryThisMethod(Foo a) 
{ 
    try 
    { 
     return ThisMethodMayThrowExceptions(a); 
    } 
    catch(BarNotFoundException) 
    { 
     return Bar.Default; 
    } 
} 

Es cierto que rara vez desea utilizar esta técnica. Se siente como un abuso de excepciones en general, pero a veces hay API que no le dejan otra opción.

(que había casi seguro que lo puso en un método separado en lugar de ponerlo "en línea" como una expresión lambda sin embargo.)

+0

¿Qué considera un abuso de excepción? En mi lógica comercial, tengo un método para calcular el salario de un empleado. Hay varias cosas que pueden salir mal, como que el empleado no tenga un conjunto de sueldos. Hice una excepción personalizada que puedo lanzar y atrapar en mi controlador y poner en mi modelo de vista. ¿Eso es malo? – Pluc

+0

@Pluc: ¿Se supone * que el empleado * tenga un salario establecido? Si es así, el hecho de que falte suena excepcional. Si se espera algo, probablemente no use excepciones para manejarlo. –

2

Cuando se trata de LINQ podrás encontrar comúnmente escenarios donde su expresión podría producir no deseado efectos secundarios. Como dijo Jon, la mejor manera de combatir este tipo de problemas es tener métodos de utilidad que pueda usar su expresión LINQ que los manejará con gracia y de una manera que no explote su código. Por ejemplo, tengo un método que he tenido que usar de vez en cuando que envuelve un TryParse para decirme si algo es un número. Hay muchos otros ejemplos por supuesto.

Una de las limitaciones de la sintaxis de expresión es que hay muchas cosas que no puede hacer bien o incluso sin interrumpir la ejecución de la expresión temporalmente para manejar un escenario determinado. El análisis de un subconjunto de elementos en un archivo XML es un maravilloso ejemplo. Intente analizar una colección principal compleja con subconjuntos secundarios de un archivo XML dentro de una única expresión y pronto se encontrará escribiendo varias piezas de expresión que se unirán para formar toda la operación.

4

Una variante de solución de Stefan para la comprensión de sintaxis:

from a in myEnumerable 
select (new Func<myType>(() => { 
    try 
    { 
     return ThisMethodMayThrowExceptions(a)); 
    } 
    catch(Exception) 
    { 
     return defaultValue; 
    } 
}))(); 

Aunque, "huele" demasiado, pero aún así a veces este método puede ser utilizado para ejecutar código con efectos secundarios dentro de la expresión.

3

En caso de que necesite la expresión en lugar de la función lambda (p.cuando se selecciona de IQueryable), se puede usar algo como esto:

public static class ExpressionHelper 
{ 
    public static Expression<Func<TSource, TResult>> TryDefaultExpression<TSource, TResult>(Expression<Func<TSource, TResult>> success, TResult defaultValue) 
    { 
     var body = Expression.TryCatch(success.Body, Expression.Catch(Expression.Parameter(typeof(Exception)), Expression.Constant(defaultValue, typeof (TResult)))); 
     var lambda = Expression.Lambda<Func<TSource, TResult>>(body, success.Parameters); 

     return lambda; 
    } 
} 

Uso:

[Test] 
public void Test() 
{ 
    var strings = new object [] {"1", "2", "woot", "3", Guid.NewGuid()}.AsQueryable(); 
    var ints = strings.Select(ExpressionHelper.TryDefaultExpression<object, int>(x => Convert.ToInt32(x), 0)); 
    Assert.IsTrue(ints.SequenceEqual(new[] {1, 2, 0, 3, 0})); 
} 
1

He venido con una pequeña extensión cuando rápidamente quiero probar/captar cada iteración de un IEnumerable<T>

Uso

public void Test() 
{ 
    List<string> completedProcesses = initialEnumerable 
     .SelectTry(x => RiskyOperation(x)) 
     .OnCaughtException(exception => { _logger.Error(exception); return null; }) 
     .Where(x => x != null) // filter the ones which failed 
     .ToList(); 
} 

La extensión

public static class OnCaughtExceptionExtension 
{ 
    public static IEnumerable<SelectTryResult<TSource, TResult>> SelectTry<TSource, TResult>(this IEnumerable<TSource> enumerable, Func<TSource, TResult> selector) 
    { 
     foreach (TSource element in enumerable) 
     { 
      SelectTryResult<TSource, TResult> returnedValue; 
      try 
      { 
       returnedValue = new SelectTryResult<TSource, TResult>(element, selector(element), null); 
      } 
      catch (Exception ex) 
      { 
       returnedValue = new SelectTryResult<TSource, TResult>(element, default(TResult), ex); 
      } 
      yield return returnedValue; 
     } 
    } 

    public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<Exception, TResult> exceptionHandler) 
    { 
     return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.CaughtException)); 
    } 

    public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<TSource, Exception, TResult> exceptionHandler) 
    { 
     return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.Source, x.CaughtException)); 
    } 

    public class SelectTryResult<TSource,TResult> 
    { 
     internal SelectTryResult(TSource source, TResult result, Exception exception) 
     { 
      Source = source; 
      Result = result; 
      CaughtException = exception; 
     } 

     public TSource Source { get; private set; } 
     public TResult Result { get; private set; } 
     public Exception CaughtException { get; private set; } 
    } 
} 

Podríamos finalmente ir un poco más por tener una extensión SkipOnException, aceptando opcionalmente un manejador de excepciones por ejemplo.