您好,登錄后才能下訂單哦!
本篇內容介紹了“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; } } /// <summary> /// 比如需要把teacher的某些屬性值賦給student,而id不需要賦值 /// </summary> /// <param name="student"></param> /// <param name="teacher"></param> 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 { /// <summary> /// 緩存表達式 /// </summary> /// <typeparam name="TSource"></typeparam> /// <typeparam name="TTarget"></typeparam> public static class MapperAccessor<TSource, TTarget> { private static Action<TSource, TTarget, string[]> 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<PropertyInfo> propertyInfos = new List<PropertyInfo>(); 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<BlockExpression> bs = new List<BlockExpression>(); 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<Action<TSource, TTarget, string[]>>(Expression.Block(bs), s, t, ps); func = lambda.Compile(); } func.Invoke(source, target, properties); return source; } } /// <summary> /// 通過目標類更新源類同名屬性值 /// </summary> /// <typeparam name="TSource">待更新的數據類型</typeparam> /// <typeparam name="TTarget">目標數據類型</typeparam> /// <param name="source">源數據</param> /// <param name="target">目標數據</param> /// <param name="properties">要變更的屬性名稱</param> /// <returns>返回源數據,更新后的</returns> public static TSource SetProperties<TSource, TTarget>(this TSource source, TTarget target, params string[] properties) { return MapperAccessor<TSource, TTarget>.Set(source, target, properties); } }
編寫測試方法
/// <summary> /// 比如需要把teacher的某些屬性值賦給student,而id不需要賦值 /// </summary> /// <param name="student"></param> /// <param name="teacher"></param> 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<Student>, List<Teacher>) CreateData(int length) { var rd = new Random(); (List<Student>, List<Teacher>) ret; ret.Item1 = new List<Student>(); ret.Item2 = new List<Teacher>(); 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#如何使用表達式樹動態更新類的屬性值”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。