将 LINQ 的 Zip 与不返回值的闭包一起使用
Posted
技术标签:
【中文标题】将 LINQ 的 Zip 与不返回值的闭包一起使用【英文标题】:Using LINQ's Zip with a closure that doesn't return a value 【发布时间】:2012-06-26 18:24:20 【问题描述】:免责声明:这个问题是出于我个人的好奇心,而不是完成某事的实际需要。所以我的例子是人为的。 不过,我认为这是一个很可能会出现的问题。
假设我们正在使用Zip 迭代两个序列,调用一个 void 方法,如果发现这对中的一项与另一项不同(因此丢弃任何返回值),该方法只会引发异常。这里的重点不是该方法抛出异常,而是它返回 void。
换句话说,我们有点像在两个集合上做一个ForEach
(顺便说一句,我知道Eric Lippert thinks about ForEach
,完全同意他的观点,从不使用它)。
现在,Zip
想要一个Func<TFirst, TSecond, TResult>
,所以当然传递与Action<TFirst, TSecond>
等效的东西是行不通的。
我的问题是:有没有比这更好的惯用方式(即返回一个虚拟值)?
var collection1 = new List<int>() ... ;
var collection2 = new List<int>() ... ;
collection1.Zip(collection2, (first, second) =>
VoidMethodThatThrows(first, second);
return true;
);
【问题讨论】:
【参考方案1】:使用Zip()
将项目扔到一个对象中,然后以您选择的方式执行foreach
(请执行正常的foreach
循环,而不是坏 ToList/ForEach 组合) .
var items = collection1.Zip(collection2, (x, y) => new First = x, Second = y );
foreach (var item in items)
VoidMethodThatThrows(item.First, item.Second);
从 C# 7.0 开始,改进的元组支持和解构使其更易于使用。
var items = collection1.Zip(collection2, (x, y) => (x, y));
// or collection1.Zip(collection2, ValueTuple.Create);
foreach (var (first, second) in items)
VoidMethodThatThrows(first, second);
此外,.NET Core 和 5 添加了一个重载,可自动将值配对到元组中,因此您不必进行映射。
var items = collection1.Zip(collection2); // IEnumerable<(Type1, Type2)>
.NET 6 添加了第三个集合。
var items = collection1.Zip(collection2, collection3); // IEnumerable<(Type1, Type2, Type3)>
【讨论】:
【参考方案2】:我经常需要对两个集合中的每一对执行动作。 Zip 方法在这种情况下没有用。
可以使用这种扩展方法ForPair:
public static void ForPair<TFirst, TSecond>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second,
Action<TFirst, TSecond> action)
using (var enumFirst = first.GetEnumerator())
using (var enumSecond = second.GetEnumerator())
while (enumFirst.MoveNext() && enumSecond.MoveNext())
action(enumFirst.Current, enumSecond.Current);
所以对于你的例子,你可以写:
var collection1 = new List<int>() 1, 2 ;
var collection2 = new List<int>() 3, 4 ;
collection1.ForPair(collection2, VoidMethodThatThrows);
【讨论】:
以上是关于将 LINQ 的 Zip 与不返回值的闭包一起使用的主要内容,如果未能解决你的问题,请参考以下文章