将 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 与不返回值的闭包一起使用的主要内容,如果未能解决你的问题,请参考以下文章

是否可以将与不同行名关联的值添加在一起,同时保持其他行不变?

Mysql带返回值与不带返回值的2种存储过程

闭包的概念与不闭包的区别(JavaScript)

闭包的概念与不闭包的区别(JavaScript)

闭包函数 装饰器

何时使用 .First 以及何时将 .FirstOrDefault 与 LINQ 一起使用?