是否有可能在LINQ查询中处理异常?
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