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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/53911.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

个人健康信息系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;健康分析师管理&#xff0c;健康手册管理&#xff0c;健康饮食管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;健康手册&#xff0c;健康饮食&…

从Apple Intelligence到IoT Intelligence,端侧生成式AI时代加速到来

9月10日凌晨1点&#xff0c;苹果新品发布会如期举行&#xff0c;全新iPhone16系列成为苹果生态中真正意义上的第一款原生AI手机&#xff0c;在第二代3nm工艺A18和A18 Pro芯片的加持下&#xff0c;iPhone16系列能够容纳并快速运行以Apple Intelligence为中心的生成式AI功能在手机…

Python 基本库用法:数学建模

文章目录 前言数据预处理——sklearn.preprocessing数据标准化数据归一化另一种数据预处理数据二值化异常值处理 numpy 相关用法跳过 nan 值的方法——nansum和nanmean展开多维数组&#xff08;变成类似list列表的形状&#xff09;重复一个数组——np.tile 分组聚集——pandas.…

从“游戏科学”到玄机科技:《黑神话:悟空》的视角打开动漫宇宙

近日&#xff0c;中国游戏界迎来了一场前所未有的盛事——由游戏科学公司开发的《黑神话&#xff1a;悟空》正式上线&#xff0c;并迅速成为全球玩家热议的焦点。在居高不下的讨论热度中&#xff0c;有人说他的成功在于对《西游记》为背景进行改编&#xff0c;对原著进行了分析…

数据分析与挖掘课程相关资源

这是在gitee上整的关于这门课的一个开源项目。 https://gitee.com/rainpet/python-data-analysis-and-mining-demo 头歌平台。 常见问题&#xff1a; 1、如何确认conda的版本&#xff0c;执行如下命令&#xff1a; conda list anaconda$2、实验室登陆后&#xff0c;无法上网&a…

基于Java+SpringBoot+Vue+MySQL的智能菜谱推荐管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 基于SpringBootVue的智能菜谱推荐管理系统【附源码文档】、…

“汉语新解” Prompt新高度,火爆的李继刚

“汉语新解” prompt 是由李继刚设计的一个用于启发人工智能模型进行创意性文本生成的指令模板。这个 prompt 的设计初衷是为了让AI能够以一种独特的方式解析和重新诠释常见的中文词汇&#xff0c;从而产生出具有深刻洞察力和幽默感的文本内容&#xff0c;仿佛是由鲁迅或林语堂…

装杯 之 Linux 指令1

hello&#xff0c;欢迎来到linux世界&#xff0c;在害没有学习linux时&#xff0c;看到别人操作&#xff0c;网课&#xff0c;真高级&#xff0c;感觉好厉害&#xff0c;就是说白了&#xff0c;看起来牛逼。ok&#xff0c;接下来&#xff0c;请大佬们进入linux之旅。 1.ls指令…

mfc140u.dll错误是什么情况?如何将mfc140u.dll丢失的解决方法详细分析

mfc140u.dll是 Microsoft Foundation Class (MFC) 库的一部分&#xff0c;通常与 Visual Studio 2015 及其后续版本相关联。如果系统中缺少 mfc140u.dll&#xff0c;可能会导致依赖该库的应用程序无法启动&#xff0c;并显示错误消息&#xff0c;如“程序无法启动&#xff0c;因…

分类预测|基于麻雀优化支持向量机的Adaboost集成的数据分类预测Matlab程序SSA-SVM-Adaboost

分类预测|基于麻雀优化支持向量机的Adaboost集成的数据分类预测Matlab程序SSA-SVM-Adaboost 文章目录 一、基本原理SSA-SVM-Adaboost 分类预测原理和流程总结 二、实验结果三、核心代码四、代码获取五、总结 一、基本原理 SSA-SVM-Adaboost 分类预测原理和流程 1. 麻雀优化算…

开源FormCreate低代码表单组件的配置项和事件的详解

在使用开源FormCreate低代码表单时&#xff0c;您可以通过各种 props 来定制表单的行为和外观。这些参数允许您控制表单的生成规则、配置选项、双向数据绑定等&#xff0c;为复杂的表单场景提供了强大的支持。 源码地址: Github | Gitee FormCreate组件Props 以下是常用的 pr…

由于安装nvm 引发的vue : 无法将“vue”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。

&#x1f388;亲爱的读者朋友们&#xff1a; 如果你觉得这篇文章对你有所帮助&#xff0c;恳请你为我点个赞&#x1f44d;。你的每一个赞都是对我辛勤创作的认可&#xff0c;是我继续前行的动力源泉。 同时&#xff0c;也欢迎你关注我的 CSDN 博客。在这里&#xff0c;我会持续…

探索数据可视化的奥秘:Seaborn库的魔力

文章目录 探索数据可视化的奥秘&#xff1a;Seaborn库的魔力背景&#xff1a;为何选择Seaborn&#xff1f;Seaborn是什么&#xff1f;如何安装Seaborn&#xff1f;简单函数介绍与示例场景应用示例常见问题与解决方案总结 探索数据可视化的奥秘&#xff1a;Seaborn库的魔力 背景…

深度剖析iOS渲染

iOS App 图形图像渲染的基本流程&#xff1a; 1.CPU&#xff1a;完成对象的创建和销毁、对象属性的调整、布局计算、文本的计算和排版、图片的格式转换和解码、图像的绘制。 2.GPU&#xff1a;GPU拿到CPU计算好的显示内容&#xff0c;完成纹理的渲染&#xff0c; 渲染完成后将渲…

c# Csv文件读写示例,如果文件存在追加写入

功能 1.写入 2.读取 导出文件效果 调用示例 注意示例中的ToDataTable()方法是自己的封装的扩展方法&#xff0c;源码在集合扩展方法-CSDN博客 private List<MarkDataModel> createMarkDataList(int count){var markDataModels new List<MarkDataModel>();for (…

RTMP播放器延迟最低可以做到多少?

技术背景 RTMP播放器的延迟可以受到多种因素的影响&#xff0c;包括网络状况、推流设置、播放器配置以及CDN分发等。因此&#xff0c;RTMP播放器的延迟并不是一个固定的数值&#xff0c;而是可以在一定范围内变化的。 正常情况下&#xff0c;网上大多看到的&#xff0c;针对R…

小琳AI课堂:LLaMA 3.1 开源大模型的全新里程碑

引言 大家好&#xff0c;这里是小琳AI课堂&#xff01;今天我们要聊的是Meta最新发布的开源大模型LLaMA 3.1。这个版本在AI界掀起了不小的波澜&#xff0c;不仅在参数规模上有显著提升&#xff0c;还在多项性能上实现了突破。让我们一起来看看LLaMA 3.1带来的新变化和意义吧&a…

爆改YOLOv8|利用SCConv改进yolov8-即轻量又涨点

1&#xff0c;本文介绍 SCConv&#xff08;空间和通道重构卷积&#xff09;是一种高效的卷积模块&#xff0c;旨在优化卷积神经网络&#xff08;CNN&#xff09;的性能&#xff0c;通过减少空间和通道的冗余来降低计算资源的消耗。该模块由两个核心组件构成&#xff1a; 空间重…

PHP轻量级高性能HTTP服务框架 - webman

摘要 webman 是一款基于 workerman 开发的高性能 HTTP 服务框架。webman 用于替代传统的 php-fpm 架构&#xff0c;提供超高性能可扩展的 HTTP 服务。你可以用 webman 开发网站&#xff0c;也可以开发 HTTP 接口或者微服务。 除此之外&#xff0c;webman 还支持自定义进程&am…

Obsidian git sync error / Obsidian git 同步失敗

Issue: commit due to empty commit message Solution 添加commit資訊&#xff0c;確保不留空白 我的設置&#xff1a;auto-backup: {{hostname}}/{{date}}/