2009-08-18 17 views

risposta

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

Ma in realtà, ha un odore.

Sintassi lambda:

x => x.something 

è una specie di scorciatoia e potrebbe essere scritto come

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

chiamata una proiezione che ha che try/catch:

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

... 

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

Devo ammettere che lo raramente voglio usare questa tecnica. Sembra un abuso di eccezioni in generale, ma a volte ci sono API che non ti lasciano scelta.

(avevo quasi certamente messo in un metodo separato invece di metterla "in linea", come un'espressione lambda però.)

+0

Che cosa consideri un abuso di eccezione? Nella mia logica aziendale, ho un metodo per calcolare la retribuzione di un dipendente. Ci sono varie cose che possono andare storte, come il dipendente che non ha un salario. Ho fatto un'eccezione personalizzata che posso lanciare e prendere nel mio controller e inserire il mio modello di visualizzazione. Questo è male? – Pluc

+0

@Pluc: il dipendente * intende * avere un salario? Se è così, mancare sembra eccezionale. Se è in qualche modo previsto, probabilmente non utilizzerei eccezioni per gestirlo. –

2

Quando si tratta di LINQ ti comunemente trovare scenari in cui la tua espressione potrebbe produrre indesiderati effetti collaterali. Come ha detto Jon, il modo migliore per combattere questo tipo di problemi è quello di utilizzare metodi di utilità che l'espressione LINQ può utilizzare per gestirli con garbo e in modo tale da non far esplodere il codice. Ad esempio, ho un metodo che ho dovuto usare di volta in volta che racchiude un TryParse per dirmi se qualcosa è un numero. Ci sono molti altri esempi, naturalmente.

Uno dei limiti della sintassi dell'espressione è che ci sono molte cose che non può fare né con grazia né per niente senza interrompere temporaneamente l'esecuzione dell'espressione per gestire un determinato scenario. L'analisi di un sottoinsieme di elementi in un file XML è un esempio meraviglioso. Prova ad analizzare una raccolta genitore complessa con sottoinsiemi secondari da un file XML all'interno di una singola espressione e ti ritroverai presto a scrivere diversi pezzi di espressione che si uniscono per formare l'intera operazione.

4

Una variante della soluzione Stefan per la sintassi di comprensione:

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

Anche se, "odori" troppo, ma ancora questo approccio a volte può essere utilizzato per l'esecuzione di codice con effetti collaterali all'interno di espressione.

3

Nel caso in cui sia necessaria l'espressione anziché la funzione lambda (ad es.quando si seleziona da IQueryable), si può usare qualcosa di simile:

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; 
    } 
} 

Usage:

[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

Sono venuto con una piccola estensione quando voglio rapidamente a try/catch ad ogni iterazione di 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(); 
} 

L'estensione

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; } 
    } 
} 

Potremmo eventualmente andare un po 'più avendo un'estensione SkipOnException, accettando facoltativamente un gestore di eccezioni per esempio.

Problemi correlati