2009-08-18 87 views

回答

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

但實際上,它有一些異味。

關於lambda語法:

x => x.something 

是怎樣的一個快捷方式,並可以寫成

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

呼叫其具有的try/catch投影:

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

... 

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

無可否認我會很少想使用這種技術。這感覺就像是一般的濫用例外情況,但有時候會有API讓你別無選擇。

(我幾乎肯定是把它放在一個單獨的方法,而不是把它「內聯」作爲lambda表達式雖然)。

+0

你認爲是什麼濫用異常?在我的業務邏輯中,我有一種計算員工薪酬的方法。有很多事情可能會出現問題,例如員工沒有工資。我做了一個自定義的異常,我可以把它放在我的控制器中並放入我的視圖模型中。那很糟? – Pluc 2015-09-10 19:09:28

+0

@Pluc:員工*是否意味着*有薪水?如果是這樣,它缺少聲音例外。如果有點期待,我可能不會使用異常來處理它。 – 2015-09-10 19:10:29

2

當LINQ打交道,你會發現通常情況下在您的表達可能會產生不希望副作用。正如喬恩所說,解決這些問題的最好方法是使用LINQ表達式可以使用的實用方法,這些方法可以優雅地處理這些問題,而且不會破壞代碼。例如,我有一個方法,我不得不使用時間來包裝一個TryParse,告訴我是否有數字。當然還有很多其他的例子。

表達式語法的侷限性之一是它有許多事情不能正常執行,甚至根本不能執行超出表達式的臨時表達式來處理給定的場景。解析XML文件中的項目子集是一個很好的例子。嘗試使用單個表達式中的XML文件解析複雜的父集合和子集合,然後您很快就會發現自己編寫了幾個表達部分,這些表達部分聚集在一起形成整個操作。

4

斯特凡對理解語法的解決方案的一個變化:

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

雖然,它「聞香」過,但還是有時可以用於裏面表達的副作用運行的代碼這種方法。

3

如果您需要表達式而不是lambda函數(例如,從IQueryable的選擇時),你可以使用這樣的事情:

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

用法:

[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

我都設有一個小擴展名,當我趕緊想嘗試/捕獲一個IEnumerable<T>

的每一次迭代

使用

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

擴展

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

我們最終可能進一步走位由具有SkipOnException擴展,接受可選例如異常處理程序。