C#如何使用表达式树动态更新类的属性值
发表于:2024-11-17 作者:千家信息网编辑
千家信息网最后更新 2024年11月17日,本篇内容介绍了"C#如何使用表达式树动态更新类的属性值"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
千家信息网最后更新 2024年11月17日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安全错误
数据库的锁怎样保障安全
八猫服务器
软件开发部门对学校有哪方面
记叙文课件软件开发
农村人才资源数据库
点点数据库
高一化学视频软件开发
车别识别数据库
南通定制软件开发
网络安全机制名词解释
查找不同数据库对象的大小
关于网络安全的校园剧本
怎么连接外国服务器
r连接db2数据库
青岛乾飞来网络技术有限公司
棋类软件开发平台
有哪些新型的软件开发技术
数据库的索引写法
极速云服务器
潍坊互联网科技学院
杭州erp软件开发哪家好
常用的软件开发模式
阿里巴巴网络技术公司地址
深圳大湾区网络安全
上海什么是软件开发管理模式
查找不同数据库对象的大小
印刷报价软件开发公司
洛阳巨人网络安全手抄报
网络技术与信息工程学院招生
成都信息安全培训网络安全培训
互联网大会上黑科技