在C#开发场景里,同时遍历两个集合并合并对应位置数据是常见需求,比如将两个分别存储姓名和年龄的列表组合成完整用户信息,或者将两组数值按位计算求和。使用LINQ的Zip方法可以用极简的代码实现这个逻辑,避免手写下标循环带来的冗余和越界风险。

Zip方法的基本定义
Zip是LINQ提供的扩展方法,主要用于将两个序列的对应元素进行合并处理。它的核心逻辑是:同时遍历两个输入集合,取相同索引位置的两个元素,传入用户指定的委托函数进行处理,最终返回处理后的新序列。如果两个集合长度不同,Zip只会处理到较短集合的长度位置,超出部分会被自动忽略。
Zip方法有两个常用的重载形式:
- 第一个重载接收两个集合和一个合并函数,返回合并后的结果集合
- 第二个重载是泛型版本,可以指定三个类型参数分别对应第一个集合元素类型、第二个集合元素类型、合并结果类型
基础使用示例
下面通过一个简单例子演示Zip的基本用法,假设我们有两个集合,一个存储学生姓名,一个存储对应的考试成绩,需要将两者合并成完整的学生成绩信息。
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
// 定义两个待合并的集合
List<string> studentNames = new List<string> { "张三", "李四", "王五" };
List<int> studentScores = new List<int> { 85, 92, 78 };
// 使用Zip方法合并两个集合
var studentInfos = studentNames.Zip(studentScores, (name, score) => $"学生姓名:{name},考试成绩:{score}");
// 遍历输出合并结果
foreach (var info in studentInfos)
{
Console.WriteLine(info);
}
}
}
上述代码执行后,会输出三个学生的姓名和对应成绩,因为两个集合长度相同,所有元素都会被处理。合并函数的两个参数分别对应两个集合相同索引位置的元素,我们可以在函数内自定义合并逻辑。
处理长度不一致的集合
当两个输入集合的长度不同时,Zip方法只会处理到较短集合的最后一个元素,较长集合超出长度的部分会被直接忽略,不会抛出异常。下面通过示例验证这个规则:
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
List<string> colors = new List<string> { "红", "黄", "蓝", "绿" };
List<string> fruits = new List<string> { "苹果", "香蕉", "橙子" };
// 两个集合长度不同,Zip只会处理前3对元素
var colorFruitPairs = colors.Zip(fruits, (color, fruit) => $"{color}色的{fruit}");
foreach (var pair in colorFruitPairs)
{
Console.WriteLine(pair);
}
// 输出结果只有3条,不会处理绿和空元素的组合
}
}
如果业务需要处理长度不一致的场景,比如用默认值填充较短集合的空缺,就需要先对集合做长度补齐处理,再调用Zip方法。
复杂对象的合并场景
实际开发中更多是合并复杂对象集合,比如有两个对象集合,一个存储用户基础信息,一个存储用户扩展信息,需要将两者合并成完整的用户对象。示例如下:
using System;
using System.Collections.Generic;
using System.Linq;
// 定义基础信息类
public class UserBase
{
public int Id { get; set; }
public string Name { get; set; }
}
// 定义扩展信息类
public class UserExt
{
public int Id { get; set; }
public string Phone { get; set; }
}
// 定义合并后的完整用户类
public class FullUser
{
public int Id { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
}
class Program
{
static void Main()
{
List<UserBase> baseList = new List<UserBase>
{
new UserBase { Id = 1, Name = "张三" },
new UserBase { Id = 2, Name = "李四" }
};
List<UserExt> extList = new List<UserExt>
{
new UserExt { Id = 1, Phone = "13800138000" },
new UserExt { Id = 2, Phone = "13900139000" }
};
// 合并两个对象集合,生成完整用户对象
List<FullUser> fullUsers = baseList.Zip(extList, (baseItem, extItem) => new FullUser
{
Id = baseItem.Id,
Name = baseItem.Name,
Phone = extItem.Phone
}).ToList();
foreach (var user in fullUsers)
{
Console.WriteLine($"ID:{user.Id},姓名:{user.Name},电话:{user.Phone}");
}
}
}
Zip方法的使用注意事项
- Zip方法不会修改原始的两个集合,而是返回一个新的序列,原始集合保持不变
- 合并函数的参数顺序和Zip方法接收的两个集合顺序一致,第一个参数对应第一个集合的元素,第二个参数对应第二个集合的元素
- 如果需要同时合并三个或更多集合,可以多次调用Zip方法,比如先合并前两个集合,再将结果和第三个集合用Zip合并
- Zip方法是延迟执行的,只有在遍历结果序列的时候才会真正执行合并逻辑,和大多数LINQ方法的执行特性一致
多集合合并的扩展示例
如果需要同时合并三个集合,可以通过链式调用Zip实现,示例如下:
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
List<string> names = new List<string> { "张三", "李四" };
List<int> ages = new List<int> { 20, 22 };
List<string> cities = new List<string> { "北京", "上海" };
// 链式调用Zip合并三个集合
var userInfos = names.Zip(ages, (n, a) => new { Name = n, Age = a })
.Zip(cities, (temp, c) => $"姓名:{temp.Name},年龄:{temp.Age},城市:{c}");
foreach (var info in userInfos)
{
Console.WriteLine(info);
}
}
}
通过上述示例可以看出,LINQ的Zip方法可以大幅简化同时遍历两个集合合并数据的代码逻辑,减少出错概率,是C#开发中处理这类场景的实用工具。