C#如何使用表达式树动态更新类的属性值
发表于:2025-02-02 作者:千家信息网编辑
千家信息网最后更新 2025年02月02日,本篇内容介绍了"C#如何使用表达式树动态更新类的属性值"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
千家信息网最后更新 2025年02月02日C#如何使用表达式树动态更新类的属性值
本篇内容介绍了"C#如何使用表达式树动态更新类的属性值"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
C#的λ表达式树是一个好东西,也是别的语言学不来的,熟悉掌握λ表达式就能够实现各种场景的个性化操作,如动态拼接查询条件、排序方式等,也能够实现替代反射的高性能操作,比如我们常用到的IQueryable和IEnumerable,每个扩展方法就全是λ表达式树。
本文给大家分享C#使用表达式树(LambdaExpression)动态更新类的属性值的相关知识,在某些业务中会遇到需要同步两个类的属性值的情况,而且有些字段是要过滤掉的。如果手动赋值则需要写很多重复的代码:
public class Teacher { public Guid Id { get; set; } public string Name { get; set; } public string Age { get; set; } } public class Student { public Guid Id { get; set; } public string Name { get; set; } public string Age { get; set; } } ////// 比如需要把teacher的某些属性值赋给student,而id不需要赋值 /// /// /// public static void SetProperty(Student student, Teacher teacher) { if (student.Name != teacher.Name) { student.Name = teacher.Name; } if (student.Age != teacher.Age) { student.Age = teacher.Age; } }
使用反射的话性能考虑,尝试写一个扩展方法使用lambda表达式树去构建一个方法
public static class ObjectExtensions { ////// 缓存表达式 /// ////// public static class MapperAccessor { private static Action func { get; set; } public static TSource Set(TSource source, TTarget target, params string[] properties) { if (func == null) { var sourceType = typeof(TSource); var targetType = typeof(TTarget); BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public; if (properties.Length == 0) { //get all properties if (sourceType == targetType) { //如果是相同类型则获取所有属性 properties = sourceType.GetProperties(bindingFlags).Select(x => x.Name) .ToArray(); } else { //如果没有传指定的属性则默认获取同名属性 List propertyInfos = new List (); foreach (var property in sourceType.GetProperties(bindingFlags)) {//不同类型指定同名且类型相同的属性 var targetProperty = targetType.GetProperty(property.Name, bindingFlags); if (targetProperty != null && targetProperty.PropertyType == property.PropertyType) { propertyInfos.Add(property); } } properties = propertyInfos.Select(x => x.Name).ToArray(); } } //定义lambda 3个参数 var s = Expression.Parameter(typeof(TSource), "s"); var t = Expression.Parameter(typeof(TTarget), "t"); var ps = Expression.Parameter(typeof(string[]), "ps"); //获取泛型扩展方法Contains var methodInfo = typeof(Enumerable).GetMethods().FirstOrDefault(e => e.Name == "Contains" && e.GetParameters().Length == 2); if (methodInfo == null) { // properties.Contains() throw new NullReferenceException(nameof(methodInfo)); } MethodInfo genericMethod = methodInfo.MakeGenericMethod(typeof(String));//创建泛型方法 List bs = new List (); foreach (string field in properties) { //获取两个类型里面的属性 var sourceField = Expression.Property(s, field); var targetField = Expression.Property(t, field); //创建一个条件表达式 var notEqual = Expression.NotEqual(sourceField, targetField);//sourceField!=targetField var method = Expression.Call(null, genericMethod, ps, Expression.Constant(field));//ps.Contains(f); //构建赋值语句 var ifTrue = Expression.Assign(sourceField, targetField); //拼接表达式 sourceField!=targetField&&ps.Contains(f) var condition = Expression.And(notEqual, Expression.IsTrue(method)); //判断是否相同,如果不相同则赋值 var expression = Expression.IfThen(condition, ifTrue); bs.Add(Expression.Block(expression)); } var lambda = Expression.Lambda >(Expression.Block(bs), s, t, ps); func = lambda.Compile(); } func.Invoke(source, target, properties); return source; } } /// /// 通过目标类更新源类同名属性值 /// ///待更新的数据类型 ///目标数据类型 /// 源数据 /// 目标数据 /// 要变更的属性名称 ///返回源数据,更新后的 public static TSource SetProperties(this TSource source, TTarget target, params string[] properties) { return MapperAccessor .Set(source, target, properties); } }
编写测试方法
////// 比如需要把teacher的某些属性值赋给student,而id不需要赋值 /// /// /// public static void SetProperty(Student student, Teacher teacher) { if (student.Name != teacher.Name) { student.Name = teacher.Name; } if (student.Age != teacher.Age) { student.Age = teacher.Age; } } public static void SetProperty2(Student student, Teacher teacher, params string[] properties) { var sourceType = student.GetType(); var targetType = teacher.GetType(); foreach (var property in properties) { var aP = sourceType.GetProperty(property); var bP = targetType.GetProperty(property); var apValue = aP.GetValue(student); var bpValue = bP.GetValue(teacher); if (apValue != bpValue) { aP.SetValue(student, bpValue); } } } static (List, List ) CreateData(int length) { var rd = new Random(); (List , List ) ret; ret.Item1 = new List (); ret.Item2 = new List (); for (int i = 0; i < length; i++) { Student student = new Student() { Id = Guid.NewGuid(), Name = Guid.NewGuid().ToString("N"), Age = rd.Next(1, 100) }; ret.Item1.Add(student); Teacher teacher = new Teacher() { Id = Guid.NewGuid(), Name = Guid.NewGuid().ToString("N"), Age = rd.Next(1, 100) }; ret.Item2.Add(teacher); } return ret; } static void Main(string[] args) { var length = 1000000; var data = CreateData(length); Stopwatch sw = new Stopwatch(); sw.Start(); for (int i = 0; i < length; i++) { SetProperty(data.Item1[i], data.Item2[i]); } sw.Stop(); Console.WriteLine($"手写方法耗时:{sw.ElapsedMilliseconds}ms"); data.Item1.Clear(); data.Item2.Clear(); var data2 = CreateData(length); sw.Restart(); for (int i = 0; i < length; i++) { data2.Item1[i].SetProperties(data2.Item2[i], nameof(Student.Age), nameof(Student.Name)); } data2.Item1.Clear(); data2.Item2.Clear(); sw.Stop(); Console.WriteLine($"lambda耗时:{sw.ElapsedMilliseconds}ms"); var data3 = CreateData(length); sw.Restart(); for (int i = 0; i < length; i++) { SetProperty2(data3.Item1[i], data3.Item2[i], nameof(Student.Age), nameof(Student.Name)); } sw.Stop(); Console.WriteLine($"反射耗时:{sw.ElapsedMilliseconds}ms"); data3.Item1.Clear(); data3.Item2.Clear(); Console.ReadKey(); }
可以看到性能和手写方法之间的差距,如果要求比较高还是手写方法,如果字段多的话写起来是很痛苦的事。但是日常用这个足够了,而且是扩展方法,通用性很强。
"C#如何使用表达式树动态更新类的属性值"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!
属性
表达式
方法
更新
类型
数据
动态
C#
相同
目标
知识
反射
两个
内容
字段
性能
情况
更多
条件
不同
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
校联购北京网络技术有限公司
服务器加固配置
打印标签无法联系数据库
论文献数据库是否应该收费
云服务器 腾讯
hp服务器供应商
御龙在天第一个服务器
保定哪个服务器好
网络安全为民手抄报内容
美团外卖数据库保存多久客户订单
贪心算法服务器管理
陀螺世界修改服务器密码
驱动网络安全
数据库中连接两个表的方法
shell服务器目录
梁溪区机电软件开发使用方法
tp5 数据库
新余市计算机软件开发
网页界面如何添加数据库
网络安全法所指的数据处理包括
打印标签无法联系数据库
从事erp软件开发前景
kudu 创建数据库
delphi 数据库编程
端游倩女幽魂服务器名称
青白江软件开发找哪家
网络安全通信健壮性测试技术
苏州职业大学网络技术
省统计系统网络安全知识讲座
网络技术加剧从众心理