myEnumerable.Select(a => ThisMethodMayThrowExceptions(a));
如何讓即使它引發異常工作的呢?就像一個帶有默認值的try catch塊拋出異常...
myEnumerable.Select(a => ThisMethodMayThrowExceptions(a));
如何讓即使它引發異常工作的呢?就像一個帶有默認值的try catch塊拋出異常...
myEnumerable.Select(a =>
{
try
{
return ThisMethodMayThrowExceptions(a));
}
catch(Exception)
{
return defaultValue;
}
});
但實際上,它有一些異味。
關於lambda語法:
x => x.something
是怎樣的一個快捷方式,並可以寫成
(x) => { return x.something; }
呼叫其具有的try/catch投影:
myEnumerable.Select(a => TryThisMethod(a));
...
public static Bar TryThisMethod(Foo a)
{
try
{
return ThisMethodMayThrowExceptions(a);
}
catch(BarNotFoundException)
{
return Bar.Default;
}
}
無可否認我會很少想使用這種技術。這感覺就像是一般的濫用例外情況,但有時候會有API讓你別無選擇。
(我幾乎肯定是把它放在一個單獨的方法,而不是把它「內聯」作爲lambda表達式雖然)。
當LINQ打交道,你會發現通常情況下在您的表達可能會產生不希望副作用。正如喬恩所說,解決這些問題的最好方法是使用LINQ表達式可以使用的實用方法,這些方法可以優雅地處理這些問題,而且不會破壞代碼。例如,我有一個方法,我不得不使用時間來包裝一個TryParse,告訴我是否有數字。當然還有很多其他的例子。
表達式語法的侷限性之一是它有許多事情不能正常執行,甚至根本不能執行超出表達式的臨時表達式來處理給定的場景。解析XML文件中的項目子集是一個很好的例子。嘗試使用單個表達式中的XML文件解析複雜的父集合和子集合,然後您很快就會發現自己編寫了幾個表達部分,這些表達部分聚集在一起形成整個操作。
斯特凡對理解語法的解決方案的一個變化:
from a in myEnumerable
select (new Func<myType>(() => {
try
{
return ThisMethodMayThrowExceptions(a));
}
catch(Exception)
{
return defaultValue;
}
}))();
雖然,它「聞香」過,但還是有時可以用於裏面表達的副作用運行的代碼這種方法。
如果您需要表達式而不是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}));
}
我都設有一個小擴展名,當我趕緊想嘗試/捕獲一個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
擴展,接受可選例如異常處理程序。
你認爲是什麼濫用異常?在我的業務邏輯中,我有一種計算員工薪酬的方法。有很多事情可能會出現問題,例如員工沒有工資。我做了一個自定義的異常,我可以把它放在我的控制器中並放入我的視圖模型中。那很糟? – Pluc 2015-09-10 19:09:28
@Pluc:員工*是否意味着*有薪水?如果是這樣,它缺少聲音例外。如果有點期待,我可能不會使用異常來處理它。 – 2015-09-10 19:10:29