LINQ查询执行投射,跳过或包装异常,其中源抛出IEnumerable.GetNext()

Geo*_*ett 21 c# linq ienumerable exception-handling word-wrap

我想要一个通用的解决方案,但作为一个例子,假设我有一个IEnumerable<string>,其中一些可以解析为整数,有些不能.

var strings = new string[] { "1", "2", "notint", "3" };
Run Code Online (Sandbox Code Playgroud)

显然,如果我这样做,Select(s => int.Parse(s, temp))它会在枚举时抛出异常.

在这种情况下,我可以先做.All(s => int.TryParse(s, out temp)),但我想要一个通用的解决方案,我不必枚举IEnumerable两次.

理想情况下,我希望能够执行以下操作,它调用我的魔术异常跳过方法:

// e.g. parsing strings
var strings = new string[] { "1", "2", "notint", "3" };
var numbers = strings.Select(s => int.Parse(s)).SkipExceptions();
// e.g. encountering null object
var objects = new object[] { new object(), new object(), null, new object() }
var objecttostrings = objects.Select(o => o.ToString()).SkipExceptions();
// e.g. calling a method that could throw
var myClassInstances = new MyClass[] { new MyClass(), new MyClass(CauseMethodToThrow:true) };
var myClassResultOfMethod = myClassInstances.Select(mci => mci.MethodThatCouldThrow()).SkipExceptions();
Run Code Online (Sandbox Code Playgroud)

我该如何编写SkipExceptions()扩展方法?


一个SelectSkipExceptions()方法的一些很好的答案,但我不知道是否SkipExceptions()可以创建一个方法,沿着相同的行AsParallel().

Urb*_*Esc 17

这个怎么样(你可能想给这个特殊的Select Extension一个更好的名字)

public static IEnumerable<TOutput> SelectIgnoringExceptions<TInput, TOutput>(
    this IEnumerable<TInput> values, Func<TInput, TOutput> selector)
   {
        foreach (var item in values)
        {
            TOutput output = default(TOutput);

            try
            {
                output = selector(item);
            }
            catch 
            {
                continue;
            }

            yield return output;
        }
    }
Run Code Online (Sandbox Code Playgroud)

编辑5 添加了一个using语句,感谢评论中的建议

    public static IEnumerable<T> SkipExceptions<T>(
        this IEnumerable<T> values)
    {
        using(var enumerator = values.GetEnumerator())
        {
           bool next = true;
           while (next)
           {
               try
               {
                   next = enumerator.MoveNext();
               }
               catch
               {
                   continue;
               }

               if(next) yield return enumerator.Current;
           } 
        }
    }
Run Code Online (Sandbox Code Playgroud)

但是,这依赖于尚未创建的传入IEnumerable(因此已经抛出异常)作为前面Function的列表.例如,如果你这样调用它可能不会起作用:选择(..).ToList().SkipExceptions()


Tho*_*que 7

创建一个TryParseInt返回以下内容的方法Nullable<int>:

int? TryParseInt(string s)
{
    int i;
    if (int.TryParse(s, out i))
        return i;
    return null;
}
Run Code Online (Sandbox Code Playgroud)

并在您的查询中使用它:

var numbers = strings.Select(s => TryParseInt(s))
                     .Where(i => i.HasValue)
                     .Select(i => i.Value);
Run Code Online (Sandbox Code Playgroud)

另见Bill Wagner的这篇文章,它提出了一个非常相似的案例.


现在,我不认为你可以编写类似于泛型SkipExceptions方法的东西,因为你会太晚捕获异常,它会结束Select循环......但是你可能会编写一个SelectSkipException方法:

public static IEnumerable<TResult> SelectSkipExceptions<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, TResult> selector)
{
    if (source == null)
        throw new ArgumentNullException("source");
    if (selector == null)
        throw new ArgumentNullException("selector");
    return source.SelectSkipExceptionsIterator(selector);
}

private static IEnumerable<TResult> SelectSkipExceptionsIterator<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, TResult> selector)
{
    foreach(var item in source)
    {
        TResult value = default(TResult);
        try
        {
            value = selector(item);
        }
        catch
        {
            continue;
        }
        yield return value;
    }
}
Run Code Online (Sandbox Code Playgroud)