嵌套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
,但老实说,您也可以保持原样。
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));