当前位置: 首页 > news >正文

C# 比较对象新思路,利用反射技术打造更灵活的比较工具

image

前言

嘿,大家好!如果你之前看过我分享的文章《C# 7个方法比较两个对象是否相等》,你可能会意识到对象比较在实际业务中经常出现的场景。今天,我想继续与大家分享一个在实际项目中遇到的问题。

有一次,我接手了一个别人的项目,有个新的需求,需要在更新对象信息时比较并记录差异,方便以后跟踪溯源,但我不太想修改底层的实体类,因为这可能会带来不可知的影响,这样会增加很多测试的工作量,而且,我一时也找不到适合的第三方库,因为需求要求同时把实体类的描述特性的内容一起写入数据表。

有没有一种方法,既能满足需求,又能对项目影响最小呢?

最后,我在反射技术上找到了灵感,手动造了一个轮子,封装了一个利用反射来比较对象的方法。这个方法不仅灵活,还能减少对项目的侵入性,非常适合在实际项目中使用,所以分享给大家,一起来看看吧!

封装对象比较方法

创建一个 CommonUtil 静态类,在里面编写封装方法,代码如下,留意注释

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;namespace Common.Util
{/// <summary>/// 公共工具静态类/// </summary>public static class CommonUtil{/// <summary>/// 获取指定类型的属性的 descriptionattribute 特性的值/// </summary>/// <typeparam name="T"></typeparam>/// <param name="t"></param>/// <param name="PropertyName"></param>/// <returns></returns>public static string GetClassDescriptionAttributeValue<T>(T t, string PropertyName){AttributeCollection attributes = TypeDescriptor.GetProperties(t)[PropertyName].Attributes;DescriptionAttribute descriptionAttribute = (DescriptionAttribute)attributes[typeof(DescriptionAttribute)];return descriptionAttribute.Description;}/// <summary>/// 比较同一个类型的两个对象的属性的值是否相同,/// 如果不相同,执行一个自定义的 Action/// </summary>/// <typeparam name="T">类型</typeparam>/// <param name="obj1">旧的对象</param>/// <param name="obj2">新的对象</param>/// <param name="action">自定义动作</param>/// <param name="excludePropertys">要忽略比较的字段,默认为空</param>public static void CompareClassObjectPropertys<T>(T obj1, T obj2, Action<string, string , string, string> action, string[] excludePropertys = null){// 反射获取对象的所有属性PropertyInfo[] properties = obj1.GetType().GetProperties();foreach (var po in properties){var propertyName = po.Name;// 跳过忽略比较的字段if (excludePropertys != null && excludePropertys.Length > 0){if (excludePropertys.Contains(propertyName)) continue;}// 分别获取两个对象的属性的值var oldValueObj = po.GetValue(obj1, null);var newValueObj = po.GetValue(obj2, null);if (oldValueObj == null && newValueObj == null){continue;}var oldValue = TranNull<string>(oldValueObj);var newValue = TranNull<string>(newValueObj);var propertyDesc = GetClassDescriptionAttributeValue<T>(obj1, propertyName);if (!oldValue.Equals(newValue)){// 如果字段值不相同,执行自定义操作,比如保存到数据库action(propertyName, propertyDesc, oldValue, newValue);}}}}
}

使用步骤

  1. 创建两个实体类,里面的属性有描述特性,代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel;namespace BLL.Model.ViewModels
    {/// <summary>/// 企业信息表视图模型/// </summary>public class ComViewModel{[Description("公司码")]public string COMP { get; set; }[Description("公司名称")]public string COMPNAME { get; set; }[Description("纳税人识别号")]public string TAXNO { get; set; }[Description("地址")]public string ADRF1 { get; set; }[Description("邮政编码")]public string PCOD { get; set; }[Description("电话")]public string PHN { get; set; }[Description("递送地址")]public string DELADRF { get; set; }[Description("递送邮政编码")]public string DELPCOD { get; set; }[Description("开户银行")]public string BANK { get; set; }[Description("银行账号")]public string BNKAC { get; set; }[Description("电邮")]public string EMAL { get; set; }[Description("网站")]public string WEB { get; set; }[Description("主要联络人")]public string CONTA { get; set; }[Description("状态")]public string STS { get; set; }[Description("更新用户")]public string USER { get; set; }[Description("更新时间")]public System.DateTime LMDTM { get; set; }}/// <summary>/// HTFP01H 表视图模型/// </summary>public class ComUpLogViewModel{public int LNID { get; set; }[Description("字段名")]public string FLD { get; set; }[Description("字段描述")]public string DESC { get; set; }[Description("旧值")]public string OLD { get; set; }[Description("新值")]public string ADRF { get; set; }[Description("更新用户")]public string USER { get; set; }[Description("更新时间")]public System.DateTime LMDTM { get; set; }}
    }
    
  2. 在更新数据时比较对象并记录差异

    using Common.Util;/// <summary>
    /// 更新数据私有方法
    /// </summary>
    /// <param name="oldComViewModel"></param>
    /// <param name="newComViewModel"></param>
    /// <returns></returns>
    private bool Update(ComViewModel oldComViewModel, ComViewModel newComViewModel)
    {// 1. 比较新旧两个 ViewModel 类的不同,将更改的信息放到一个集合中var list = new List<ComUpLog>();var excludeProperys = new string[9] { "USERHT01", "LMDTMHT01" };CommonUtil.CompareClassObjectPropertys<ComViewModel>(oldComViewModel,newComViewModel,(propertyName, propertyDesc, oldValue, newValue) => {var ComUpLog = new ComUpLog(){FLD = $"[{oldComViewModel.COMP}]{propertyName}",DESC = propertyDesc,OLD = oldValue,ADRF = newValue,USER = newComViewModel.USERHT01,LMDTM = DateTime.Now};list.Add(ComUpLog);}, excludeProperys);// 2. EF 批量更新数据到数据库……
    }
    

总结

通过反射技术,我们能够轻松获取对象的属性及其值,从而实现两个对象之间的比较。

这种方式非常灵活,可以达到“非侵入”的效果,对原有项目的影响降到最低。

使用这个封装的方法,你只需根据实际需求稍作修改,就能在项目中更灵活地进行对象比较。

希望这个小技巧能为你的项目带来便利!🌟

最后,如果您有更好的建议和不同的观点,欢迎留言讨论!

往期精彩

  1. C# 7个方法比较两个对象是否相等
  2. 闲话 .NET(7):.NET Core 能淘汰 .NET FrameWork 吗?
  3. 常用的 4 种 ORM 框架(EF Core,SqlSugar,FreeSql,Dapper)对比总结

我是老杨,一个执着于编程乐趣、至今奋斗在一线的 10年+ 资深研发老鸟,是软件项目管理师,也是快乐的程序猿,持续免费分享全栈实用编程技巧、项目管理经验和职场成长心得。欢迎关注老杨的公众号,更多干货等着你!

image

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【MYSQL表的增删改查(进阶)】
  • 【JS】path的使用说明
  • 105.游戏安全项目-基址的技术原理-分析技巧
  • Unity 之 【Android Unity FBO渲染】之 [Unity 渲染 Android 端播放的视频] 的一种方法简单整理
  • Centos7更换阿里云的 YUM 镜像仓库
  • 计算机网络30——Linux-gdb调试命令makefile
  • Jenkins Docker Pipeline Clone Build Deploy mysqldump
  • 企业项目中常用的表结构设计
  • 摩托罗拉大顾问ADVISOR BP机拆解评测
  • MySQL索引测试
  • 智能体时代,AI正从“神坛”走向“人间”
  • 工具集锦 论文 施工ing
  • element-plus弹窗内分页表格保留勾选项
  • k8s中的存储
  • Vim使用技巧——第一章 Vim解决问题的方式
  • [js高手之路]搞清楚面向对象,必须要理解对象在创建过程中的内存表示
  • angular学习第一篇-----环境搭建
  • create-react-app项目添加less配置
  • crontab执行失败的多种原因
  • Docker下部署自己的LNMP工作环境
  • input实现文字超出省略号功能
  • Js实现点击查看全文(类似今日头条、知乎日报效果)
  • MySQL常见的两种存储引擎:MyISAM与InnoDB的爱恨情仇
  • Python学习之路16-使用API
  • v-if和v-for连用出现的问题
  • windows下如何用phpstorm同步测试服务器
  • 解决iview多表头动态更改列元素发生的错误
  • 聊一聊前端的监控
  • 区块链将重新定义世界
  • 如何选择开源的机器学习框架?
  • 数组大概知多少
  • 协程
  • 一道闭包题引发的思考
  • 智能合约开发环境搭建及Hello World合约
  • Unity3D - 异步加载游戏场景与异步加载游戏资源进度条 ...
  • ​LeetCode解法汇总2182. 构造限制重复的字符串
  • ​LeetCode解法汇总2670. 找出不同元素数目差数组
  • ​zookeeper集群配置与启动
  • ​如何防止网络攻击?
  • ​虚拟化系列介绍(十)
  • #07【面试问题整理】嵌入式软件工程师
  • #define与typedef区别
  • #Lua:Lua调用C++生成的DLL库
  • ${ }的特别功能
  • (30)数组元素和与数字和的绝对差
  • (BFS)hdoj2377-Bus Pass
  • (C语言)字符分类函数
  • (day 2)JavaScript学习笔记(基础之变量、常量和注释)
  • (Java入门)抽象类,接口,内部类
  • (阿里云在线播放)基于SpringBoot+Vue前后端分离的在线教育平台项目
  • (八)Spring源码解析:Spring MVC
  • (附源码)springboot优课在线教学系统 毕业设计 081251
  • (汇总)os模块以及shutil模块对文件的操作
  • (一)基于IDEA的JAVA基础1
  • (转)Scala的“=”符号简介