嵌套foreach到LINQ的转换,带有两个列表上的一个条件

本文关键字:列表 两个 条件 一个 foreach 转换 嵌套 LINQ | 更新日期: 2023-09-27 18:26:40

LINQ对我来说就像是巫毒魔法。无需向你展示我所做的一切,什么都不起作用,甚至无法编译,我只是在一种有些不可理解的语言中出错:

The type arguments for method 'System.Linq.Enumerable.SelectMany<TSource,TResult>(System.Collections.Generic.IEnumerable<TSource>, System.Func<TSource,int,System.Collections.Generic.IEnumerable<TResult>>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

所以,这是我的代码:

foreach (string first in firstStrings)
    foreach (string second in secondStrings)
    {
        try {
            if (second.Contains(DoFormatting(first)))
                DoStuff(first, second);
        }
        catch (Exception e)
        {
            LogStuff(first, second);
        }
    }

(如何)将其翻译成Linq

嵌套foreach到LINQ的转换,带有两个列表上的一个条件

您可以执行以下操作将其减少为一个foreach,但老实说,您也可以保持原样。

var pairs = from first in firstStrings
            from second in secondStrings
            select new 
            { 
                first, 
                second
            };
foreach(var pair in pairs)
{
    try 
    {
        if (pair.second.Contains(DoFormatting(pair.first)))
            DoStuff(pair.first, pair.second);
    }
    catch (Exception e)
    {
        LogStuff(pair.first, pair.second);
    }
}

OR与扩展方法

var pairs = firstStrings.Join(
    secondStrings, 
    x=>true, 
    y=>true, 
    (first, second) => new { first, second});
foreach(var pair in pairs)
{
    try 
    {
        if (pair.second.Contains(DoFormatting(pair.first)))
            DoStuff(pair.first, pair.second);
    }
    catch (Exception e)
    {
        LogStuff(pair.first, pair.second);
    }
}

我不会使用ForEach扩展方法,因为在枚举上枚举并创建副作用是违反使用Linq的最佳实践的。

如果你真的想创建一个扩展方法,如下所示:

    public static class ExtensionMethods
    {
        public static void ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
        {
            foreach (T item in enumeration)
            {
                action(item);
            }
        }
    }

然后按如下方式使用LINQ:

 firstStrings.ForEach(first => secondStrings.ForEach(second =>
                {
                    try
                    {
                        if (second.Contains(DoFormatting(first)))
                        {
                            DoStuff(first, second);
                        }
                    }
                    catch(Exception e)
                    {
                        LogStuff(first, second);
                    }
                }));

但是,正如其他评论者所提到的,我会保持你的代码原样(至少在你是否将其转换为LINQ方面)。

您不能真正将其中的大部分转换为Linq,因为您仍然需要迭代所有的排列,这需要两个独立的嵌套循环。

你可以这样做,它不会节省很多,但有些:

foreach (string first in firstStrings)
    try {
        foreach (var second in secondStrings.Where(second => second.Contains(DoFormatting(first))))
           DoStuff(first, second);
    }
    catch (Exception e) {
        LogStuff(first, second);
    }

编辑:

注意,这里的日志记录必须更改,因为第二个在catch块中不可用,这是将内容移动到linq的问题。。您对集合中导致错误的项的访问权限较低。您可能需要将异常处理转移到DoFormtting和DoStuff中。

而且,正如juharr所指出的,如果抛出异常,它将终止对secondStrings的处理,而不是继续下一个处理。因此,这可能也不是所需的功能。。同样,迁移到linq的副作用是,您将失去进行细粒度异常处理和延续的能力。

这里的问题是LINQ是使用函数式编程原理设计的,最大的原则是不会造成副作用。

也就是说,结合朱哈尔和埃里克的方法,你可以做一些事情这将起作用:

        var ContainedPairs=
            firstStrings.Join(secondStrings, 
                              x => true, 
                              y => true, 
                             (first, second) => new { first, second })
                         .Where(pairs => pairs.second.Contains(DoFormatting(pairs.first)));
//loop over pairs calling dostuff and error handling

请注意,这里重要的是必须保证DoFormatting不会更改pairs.first,也不会抛出错误。

如果不是这种情况,则将DoFormatting的签名更改为以下内容:

private bool ContainsFormatted(string first, string second, out Exception ex)

会允许你这样做:

        var pairResults =
            firstStrings.Join(secondStrings, 
                              x => true, 
                              y => true, 
                              (first, second) => new { first, second })
                        .Select(pair =>
                                {
                                    Exception ex = null;
                                    var containsFormatted = ContainsFormatted(pair.second, pair.first, out ex);
                                    return new { pair, containsFormatted, ex };
                                });
//loop over pair results, calling DoStuff on items where containsFormatted = true and logging exceptions etc otherwise

这里的重点是,linq仍然忽略了副作用和错误处理,但这并不意味着你需要放弃更具功能性的编程风格的好处

我建议看一下Lambda表达式。

http://msdn.microsoft.com/en-us/library/bb397675.aspx

所以,LINQ的翻译看起来像这个

String result = second.Where( r => r.Contains(first));