导读:本期聚焦于小伙伴创作的《如何使用LINQ、Lambda表达式、委托快速比较两个集合,找出需要新增、修改、删除的对象》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何使用LINQ、Lambda表达式、委托快速比较两个集合,找出需要新增、修改、删除的对象》有用,将其分享出去将是对创作者最好的鼓励。

在C#开发中,对比两个集合的差异是常见需求,比如本地缓存数据和接口返回数据的同步、数据库存量数据和待更新数据的比对等场景,都需要找出两个集合中需要新增、修改、删除的对象。利用LINQ、Lambda表达式和委托的组合,可以简洁高效地实现这个需求,避免繁琐的嵌套循环逻辑。

如何使用LINQ、Lambda表达式、委托快速比较两个集合,找出需要新增、修改、删除的对象

核心思路设计

要实现两个集合的差异对比,首先需要明确三个核心判断逻辑:

  • 新增对象:存在于新集合,但不存在于旧集合的对象
  • 删除对象:存在于旧集合,但不存在于新集合的对象
  • 修改对象:同时存在于新旧两个集合,但关键属性发生变化,需要更新数据的对象

这里需要用到委托来定义两个对象的对比规则,以及对象属性是否发生变化的判断规则,这样可以让对比逻辑适配不同的业务对象,提升代码的复用性。

基础实体类定义

首先定义一个通用的业务实体类作为示例,实际使用时可以替换为自己的业务对象:

// 示例用户实体类
public class User
{
    // 唯一标识
    public int Id { get; set; }
    // 用户名称
    public string Name { get; set; }
    // 用户年龄
    public int Age { get; set; }
    // 用户邮箱
    public string Email { get; set; }
}

通用对比方法实现

接下来封装一个通用的集合对比方法,通过委托传入对比键的逻辑和属性变化的判断逻辑,适配不同的业务场景:

using System;
using System.Collections.Generic;
using System.Linq;

public class CollectionCompareHelper
{
    /// <summary>
    /// 对比两个集合,找出新增、修改、删除的对象
    /// </summary>
    /// <typeparam name="T">对象类型</typeparam>
    /// <param name="oldList">旧集合</param>
    /// <param name="newList">新集合</param>
    /// <param name="keySelector">对象唯一键选择委托,用于判断两个对象是否为同一个</param>
    /// <param name="isModifiedSelector">对象是否修改的判断委托,传入旧对象和新对象,返回是否发生变化</param>
    /// <returns>包含新增、修改、删除对象的元组</returns>
    public static (List<T> AddList, List<T> UpdateList, List<T> DeleteList) CompareCollections<T>(
        List<T> oldList,
        List<T> newList,
        Func<T, object> keySelector,
        Func<T, T, bool> isModifiedSelector)
    {
        // 初始化结果集合
        List<T> addList = new List<T>();
        List<T> updateList = new List<T>();
        List<T> deleteList = new List<T>();

        // 将旧集合转换为字典,键为对象的唯一标识,提升查询效率
        var oldDict = oldList.ToDictionary(keySelector);
        // 将新集合转换为字典,键为对象的唯一标识
        var newDict = newList.ToDictionary(keySelector);

        // 找出新增对象:新集合的键不在旧集合字典中
        foreach (var newItem in newDict)
        {
            if (!oldDict.ContainsKey(newItem.Key))
            {
                addList.Add(newItem.Value);
            }
        }

        // 找出删除对象:旧集合的键不在新集合字典中
        foreach (var oldItem in oldDict)
        {
            if (!newDict.ContainsKey(oldItem.Key))
            {
                deleteList.Add(oldItem.Value);
            }
        }

        // 找出修改对象:键同时存在于两个字典,且属性发生变化
        foreach (var oldItem in oldDict)
        {
            if (newDict.TryGetValue(oldItem.Key, out T newItem))
            {
                // 调用委托判断对象是否修改
                if (isModifiedSelector(oldItem.Value, newItem))
                {
                    updateList.Add(newItem);
                }
            }
        }

        return (addList, updateList, deleteList);
    }
}

方法调用示例

接下来使用上面定义的User实体类和对比方法,演示具体的使用方式:

class Program
{
    static void Main(string[] args)
    {
        // 模拟旧集合数据
        List<User> oldUsers = new List<User>
        {
            new User { Id = 1, Name = "张三", Age = 20, Email = "zhangsan@ipipp.com" },
            new User { Id = 2, Name = "李四", Age = 25, Email = "lisi@ipipp.com" },
            new User { Id = 3, Name = "王五", Age = 30, Email = "wangwu@ipipp.com" }
        };

        // 模拟新集合数据
        List<User> newUsers = new List<User>
        {
            new User { Id = 1, Name = "张三", Age = 21, Email = "zhangsan@ipipp.com" }, // 年龄修改,需要更新
            new User { Id = 2, Name = "李四", Age = 25, Email = "lisi@ipipp.com" }, // 无变化
            new User { Id = 4, Name = "赵六", Age = 28, Email = "zhaoliu@ipipp.com" } // 新增对象
            // Id=3的对象不存在,属于删除对象
        };

        // 调用对比方法,传入键选择委托和修改判断委托
        var result = CollectionCompareHelper.CompareCollections(
            oldUsers,
            newUsers,
            // 键选择:使用Id作为唯一标识
            u => u.Id,
            // 修改判断:对比Name、Age、Email三个属性是否变化,有一个变化就认为需要修改
            (oldUser, newUser) => oldUser.Name != newUser.Name 
                || oldUser.Age != newUser.Age 
                || oldUser.Email != newUser.Email
        );

        // 输出对比结果
        Console.WriteLine("新增对象:");
        foreach (var item in result.AddList)
        {
            Console.WriteLine($"Id:{item.Id}, Name:{item.Name}, Age:{item.Age}");
        }

        Console.WriteLine("n修改对象:");
        foreach (var item in result.UpdateList)
        {
            Console.WriteLine($"Id:{item.Id}, Name:{item.Name}, Age:{item.Age}");
        }

        Console.WriteLine("n删除对象:");
        foreach (var item in result.DeleteList)
        {
            Console.WriteLine($"Id:{item.Id}, Name:{item.Name}, Age:{item.Age}");
        }
    }
}

结果说明

运行上面的示例代码,输出结果如下:

  • 新增对象:Id为4的赵六,因为旧集合中不存在该Id
  • 修改对象:Id为1的张三,年龄从20变为21,属性发生变化
  • 删除对象:Id为3的王五,新集合中不存在该Id

这种实现方式的优势在于对比逻辑和具体业务对象解耦,只需要传入不同的键选择委托和修改判断委托,就可以适配各种集合对比场景,不需要为每个业务对象单独编写对比代码。同时内部使用字典存储集合数据,对比时间复杂度为O(n),比嵌套循环的O(n²)效率更高,适合处理数据量较大的集合场景。

注意事项

  • 键选择委托返回的对象需要保证可以作为字典的键,建议返回值类型或者不可变的引用类型,避免出现键重复或者键变化的问题
  • 修改判断委托的逻辑需要根据实际业务需求调整,比如有些场景只需要判断部分核心属性,不需要对比所有字段
  • 如果集合中存在重复的键,ToDictionary方法会抛出异常,使用前需要确保集合中的键唯一,或者提前做去重处理

LINQLambda_表达式委托集合比较修改时间:2026-06-29 15:33:31

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。