C# 是否可以在 LINQ 查询中处理异常?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1294251/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is it possible to handle exceptions within LINQ queries?
提问by Jader Dias
Example:
例子:
myEnumerable.Select(a => ThisMethodMayThrowExceptions(a));
How to make it work even if it throws exceptions? Like a try catch block with a default value case an exceptions is thrown...
即使抛出异常,如何使其工作?就像带有默认值情况的 try catch 块一样,会抛出异常...
采纳答案by Stefan Steinegger
myEnumerable.Select(a =>
{
try
{
return ThisMethodMayThrowExceptions(a));
}
catch(Exception)
{
return defaultValue;
}
});
But actually, it has some smell.
但实际上,它有一些气味。
About the lambda syntax:
关于 lambda 语法:
x => x.something
is kind of a shortcut and could be written as
是一种快捷方式,可以写成
(x) => { return x.something; }
回答by Jon Skeet
Call a projection which has that try/catch:
调用具有 try/catch 的投影:
myEnumerable.Select(a => TryThisMethod(a));
...
public static Bar TryThisMethod(Foo a)
{
try
{
return ThisMethodMayThrowExceptions(a);
}
catch(BarNotFoundException)
{
return Bar.Default;
}
}
Admittedly I'd rarelywant to use this technique. It feels like an abuse of exceptions in general, but sometimes there are APIs which leave you no choice.
诚然,我很少想使用这种技术。总体上感觉像是滥用异常,但有时有些 API 让您别无选择。
(I'd almost certainly put it in a separate method rather than putting it "inline" as a lambda expression though.)
(我几乎肯定会把它放在一个单独的方法中,而不是把它作为 lambda 表达式“内联”。)
回答by Nathan Taylor
When dealing with LINQ you'll commonly find scenarios where your expression could produce undesired side effects. As Jon said, the best way to combat these sort of problems is to have utility methods your LINQ expression can use that will handle these gracefully and in a fashion that won't blow up your code. For example, I have a method I've had to use time to time which wraps a TryParse to tell me if something is a number. There are many other examples of course.
在处理 LINQ 时,您通常会发现您的表达式可能会产生不良副作用的情况。正如 Jon 所说,解决这类问题的最佳方法是让您的 LINQ 表达式可以使用实用方法来优雅地处理这些问题,并且不会破坏您的代码。例如,我有一个方法我不得不不时使用它包装一个 TryParse 来告诉我某个东西是否是一个数字。当然还有很多其他的例子。
One of the limitations of the expression syntax is that there are a lot of things it can't do either gracefully or even at all without breaking execution out of the expression temporarily to handle a given scenario. Parsing a subset of items in an XML file is wonderful example. Try parsing a complex parent collection with child subsets from an XML file within a single expression and you'll soon find yourself writing several expression pieces that all come together to form the entire operation.
表达式语法的局限性之一是,如果不暂时中断表达式的执行来处理给定的场景,它就无法优雅地甚至根本无法完成很多事情。解析 XML 文件中的项目子集就是一个很好的例子。尝试在单个表达式中解析带有来自 XML 文件的子子集的复杂父集合,您很快就会发现自己编写了几个表达式片段,这些片段组合在一起形成整个操作。
回答by esteewhy
A variation of Stefan's solution for comprehension syntax:
Stefan 的理解语法解决方案的变体:
from a in myEnumerable
select (new Func<myType>(() => {
try
{
return ThisMethodMayThrowExceptions(a));
}
catch(Exception)
{
return defaultValue;
}
}))();
Although, it "smells" too, but still this approach can sometimes be used for running code with side-effects inside expression.
虽然它也“闻起来”,但这种方法有时仍可用于运行在表达式中具有副作用的代码。
回答by THTP
In case you need Expression instead of lambda function (e.g. when selecting from IQueryable), you can use something like this:
如果您需要 Expression 而不是 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;
}
}
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}));
}
回答by LeBaptiste
I have come with a small extension when I quickly want to try/catch every iteration of an IEnumerable<T>
当我很快想尝试/捕获每个迭代时,我带来了一个小的扩展 IEnumerable<T>
Usage
用法
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();
}
The extension
扩展名
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; }
}
}
We could eventually go a bit further by having a SkipOnException
extension, accepting optionally an exception handler for example.
我们最终可以通过SkipOnException
扩展来更进一步,例如接受可选的异常处理程序。