在 C# 中,Zip
是System.Linq
命名空间提供的一个扩展方法,主要用于将两个序列中相对应的元素组合在一起。下面为你详细介绍它的用法。
基本语法
csharp
序列1.Zip(序列2, (元素1, 元素2) => 结果);
参数和返回值
- 参数:
- 序列 2:用于和原序列进行配对的第二个序列。
- 结果选择器:这是一个委托,它定义了如何把两个序列中的对应元素组合成一个结果。
- 返回值:返回一个
IEnumerable<T>
序列,该序列中的元素是由两个输入序列的对应元素通过指定方式组合而成的。
方法特性
- 配对规则:
Zip
方法会按照元素在序列中的位置进行配对,也就是第一个序列的第一个元素和第二个序列的第一个元素配对,依此类推。 - 长度处理:当两个序列长度不同时,生成的序列长度以较短的那个序列为准,多余的元素会被忽略。
- 惰性执行:
Zip
方法采用的是惰性执行方式,只有在对结果进行迭代时才会真正进行计算。
适用场景
- 要将两个相关序列的元素组合成一个新序列时。
- 对两个序列进行并行遍历时。
- 把多个数据源的数据合并到一起时。
示例代码
下面通过几个例子,帮助你理解Zip
方法的常见用法。
csharp
using System;
using System.Linq;
class Program
{
static void Main()
{
// 示例1:组合两个序列的元素
int[] numbers = { 1, 2, 3 };
string[] words = { "one", "two", "three" };
var pairs = numbers.Zip(words, (num, word) => new { Number = num, Word = word });
foreach (var pair in pairs)
{
Console.WriteLine($"数字: {pair.Number}, 单词: {pair.Word}");
}
// 输出:
// 数字: 1, 单词: one
// 数字: 2, 单词: two
// 数字: 3, 单词: three
// 示例2:处理不同长度的序列
int[] longerNumbers = { 1, 2, 3, 4, 5 };
string[] shorterWords = { "一", "二", "三" };
var mixedPairs = longerNumbers.Zip(shorterWords, (num, word) => $"{num} => {word}");
foreach (var item in mixedPairs)
{
Console.WriteLine(item);
}
// 输出:
// 1 => 一
// 2 => 二
// 3 => 三
// 示例3:数学运算
double[] values1 = { 1.0, 2.0, 3.0 };
double[] values2 = { 4.0, 5.0, 6.0 };
var products = values1.Zip(values2, (a, b) => a * b);
Console.WriteLine("乘积结果: " + string.Join(", ", products));
// 输出:乘积结果: 4, 10, 18
}
}
在代码中的应用
ublic class UpdateWxMpUserInput
{
/// <summary>
/// 用户Id
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// 公众号用户Ids 多选列表里的数据
/// </summary>
public Guid[] Ids { get; set; }
public string appid { get; set; }
/// <summary>
///用户标签列表里选择ids对应openid值
/// </summary>
public string[] openid_list { get; set; }
/// <summary>
/// 用户标签值列表,逗号分隔 如:1,2,3或者空字符串
/// </summary>
public string tagid_list { get; set; }
/// <summary>
/// 用户标签列表里选择ids对应tagid_list值
/// </summary>
public string[] tagid_list_x { get; set; }
}
在提供的代码里,Zip
方法的作用是把Ids
数组和tagid_list_x
数组中相对应的元素组合成键值对,进而创建一个Dictionary<Guid, string>
。具体分析如下:
csharp
return request.Ids
.Zip(request.tagid_list_x, (id, tag) => new { id, tag })
.ToDictionary(pair => pair.id, pair => pair.tag);
- 参数验证:首先要保证两个数组都不为
null
,并且长度相同。 - 配对过程:借助
Zip
方法,将Ids
中的每个Guid
值和tagid_list_x
中对应位置的string
值组合成一个匿名类型对象。 - 字典创建:利用
ToDictionary
方法,把这些配对好的元素转换为字典,其中id
作为键,tag
作为值。
注意要点
- 序列长度要留意:要是两个序列长度不一致,那么较长序列中多余的元素会被忽略。
- 避免空引用异常:在使用
Zip
方法之前,要确保两个序列都不为null
,防止出现NullReferenceException
。 - 元素顺序很重要:
Zip
方法是按照元素的位置进行配对的,所以要保证两个序列中元素的顺序是正确的。
通过Zip
方法,你能够简洁高效地处理需要并行操作多个序列的场景。
调用微信批量为用户打标签
// 合并选择的标签并验证数量
private void MergeSelectedTagsAndValidate(Dictionary<Guid, string> userTags, UpdateWxMpUserInput input)
{
string[] selectedTags = input.select_tagid_list;
if (selectedTags == null || selectedTags.Length == 0)
return;
foreach (var userId in userTags.Keys.ToList())
{
var existingTags = string.IsNullOrEmpty(userTags[userId])
? new List<string>()
: userTags[userId].Split(',').ToList();
// 合并并去重标签
var mergedTags = existingTags.Union(selectedTags).ToList();
// 验证标签数量
if (mergedTags.Count > 20)
{
throw new UserFriendlyException($"用户 {input.openid_list[Array.IndexOf(input.Ids, userId)] } 的标签数量超过微信限制(最多20个)");
}
// 更新标签列表
userTags[userId] = string.Join(",", mergedTags);
}
}
/// <summary>
/// 批量修改公众号用户标签
/// </summary>
/// <param name="input"> tagid_lists</param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
public async Task BatchTaggingAsync(UpdateWxMpUserInput input)
{
// 步骤1将输入转换为字典(用户ID -> 标签列表)
Dictionary<Guid, string> guidToNewTagid_list = ConvertToDictionary(input);
// 步骤2: 合并选择的标签并去重,验证标签数量
MergeSelectedTagsAndValidate(guidToNewTagid_list, input);
try
{ //步骤3:
foreach (var tagid in input.select_tagid_list)
{ //步骤4
var UpdateRemarkAsync = await UserTagApi.BatchUntaggingAsync(AuthorizerAccessToken,Int32.Parse(tagid) , input.openid_list.ToList());
if (UpdateRemarkAsync.errcode != ReturnCode.请求成功)
{
throw new UserFriendlyException($"批量修改微信编号{tagid}失败: {UpdateRemarkAsync.errmsg}");
}
}
//TODO 步骤5: 更新数据库中的标签列表
//await _WxMpUserManager.UpdateTagidListAsync(guidToNewTagid_list, input.appid);
}
catch (Exception ex)
{
// _logger.LogError($"更新SPsdk用户备注异常: {ex.Message}");
}
}