乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      對象克隆(C# 快速高效率復(fù)制對象另一種方式 表達式樹轉(zhuǎn))

       _明心見性_ 2019-06-22

      1、需求

      在代碼中經(jīng)常會遇到需要把對象復(fù)制一遍,或者把屬性名相同的值復(fù)制一遍。

      比如:

      復(fù)制代碼
      復(fù)制代碼
          public class Student
          {
              public int Id { get; set; }
              public string Name { get; set; } 
              public int Age { get; set; } 
          }
      
          public class StudentSecond
          {
              public int Id { get; set; }
              public string Name { get; set; }
              public int Age { get; set; } 
          }
      復(fù)制代碼
      復(fù)制代碼

      Student s = new Student() { Age = 20, Id = 1, Name = "Emrys" };

      我們需要給新的Student賦值

      Student ss = new Student { Age = s.Age, Id = s.Id, Name = s.Name };

      再或者給另一個類StudentSecond的屬性賦值,兩個類屬性的名稱和類型一致。

      StudentSecond ss = new StudentSecond { Age = s.Age, Id = s.Id, Name = s.Name };

       

      2、解決辦法

      當(dāng)然最原始的辦法就是把需要賦值的屬性全部手動手寫。這樣的效率是最高的。但是這樣代碼的重復(fù)率太高,而且代碼看起來也不美觀,更重要的是浪費時間,如果一個類有幾十個屬性,那一個一個屬性賦值豈不是浪費精力,像這樣重復(fù)的勞動工作更應(yīng)該是需要優(yōu)化的。

      2.1、反射

      反射應(yīng)該是很多人用過的方法,就是封裝一個類,反射獲取屬性和設(shè)置屬性的值。

      復(fù)制代碼
      復(fù)制代碼
       private static TOut TransReflection<TIn, TOut>(TIn tIn)
              {
                  TOut tOut = Activator.CreateInstance<TOut>();
                  var tInType = tIn.GetType();
                  foreach (var itemOut in tOut.GetType().GetProperties())
                  {
                      var itemIn = tInType.GetProperty(itemOut.Name); ;
                      if (itemIn != null)
                      {
                          itemOut.SetValue(tOut, itemIn.GetValue(tIn));
                      }
                  }
                  return tOut;
              }
      復(fù)制代碼
      復(fù)制代碼

       

      調(diào)用:StudentSecond ss= TransReflection<Student, StudentSecond>(s);

      調(diào)用一百萬次耗時:2464毫秒

       

      2.2、序列化

      序列化的方式有很多種,有二進制、xml、json等等,今天我們就用Newtonsoft的json進行測試。

      調(diào)用:StudentSecond ss= JsonConvert.DeserializeObject<StudentSecond>(JsonConvert.SerializeObject(s));

      調(diào)用一百萬次耗時:2984毫秒

      從這可以看出序列化和反射效率差別不大。

       

      3、表達式樹

      3.1、簡介

      關(guān)于表達式樹不了解的可以百度。

      也就是說復(fù)制對象也可以用表達式樹的方式。

              Expression<Func<Student, StudentSecond>> ss = (x) => new StudentSecond { Age = x.Age, Id = x.Id, Name = x.Name };
              var f = ss.Compile();
              StudentSecond studentSecond = f(s);

      這樣的方式我們可以達到同樣的效果。

      有人說這樣的寫法和最原始的復(fù)制沒有什么區(qū)別,代碼反而變多了呢,這個只是第一步。

       

      3.2、分析代碼

      我們用ILSpy反編譯下這段表達式代碼如下:

      復(fù)制代碼
      復(fù)制代碼
         ParameterExpression parameterExpression;
          Expression<Func<Student, StudentSecond>> ss = Expression.Lambda<Func<Student, StudentSecond>>(Expression.MemberInit(Expression.New(typeof(StudentSecond)), new MemberBinding[]
          {
              Expression.Bind(methodof(StudentSecond.set_Age(int)), Expression.Property(parameterExpression, methodof(Student.get_Age()))),
              Expression.Bind(methodof(StudentSecond.set_Id(int)), Expression.Property(parameterExpression, methodof(Student.get_Id()))),
              Expression.Bind(methodof(StudentSecond.set_Name(string)), Expression.Property(parameterExpression, methodof(Student.get_Name())))
          }), new ParameterExpression[]
          {
              parameterExpression
          });
          Func<Student, StudentSecond> f = ss.Compile();
          StudentSecond studentSecond = f(s);
      復(fù)制代碼
      復(fù)制代碼

      那么也就是說我們只要用反射循環(huán)所有的屬性然后Expression.Bind所有的屬性。最后調(diào)用Compile()(s)就可以獲取正確的StudentSecond。

      看到這有的人又要問了,如果用反射的話那豈不是效率很低,和直接用反射或者用序列化沒什么區(qū)別嗎?

      當(dāng)然這個可以解決的,就是我們的表達式樹可以緩存。只是第一次用的時候需要反射,以后再用就不需要反射了。

       

      3.3、復(fù)制對象通用代碼

      為了通用性所以其中的Student和StudentSecond分別泛型替換。

      復(fù)制代碼
      復(fù)制代碼
              private static Dictionary<string, object> _Dic = new Dictionary<string, object>();
      
              private static TOut TransExp<TIn, TOut>(TIn tIn)
              {
                  string key = string.Format("trans_exp_{0}_{1}", typeof(TIn).FullName, typeof(TOut).FullName);
                  if (!_Dic.ContainsKey(key))
                  {
                      ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
                      List<MemberBinding> memberBindingList = new List<MemberBinding>();
      
                      foreach (var item in typeof(TOut).GetProperties())
                      {  
        
                  if (!item.CanWrite)
                    continue; 
                          MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));
                          MemberBinding memberBinding = Expression.Bind(item, property);
                          memberBindingList.Add(memberBinding);
                      }
      
                      MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());
                      Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[] { parameterExpression });
                      Func<TIn, TOut> func = lambda.Compile();
      
                      _Dic[key] = func;
                  }
                  return ((Func<TIn, TOut>)_Dic[key])(tIn);
              }
      復(fù)制代碼
      復(fù)制代碼

      調(diào)用:StudentSecond ss= TransExp<Student, StudentSecond>(s);

      調(diào)用一百萬次耗時:564毫秒

       

      3.4、利用泛型的特性再次優(yōu)化代碼

      不用字典存儲緩存,因為泛型就可以很容易解決這個問題。

       

      復(fù)制代碼
      復(fù)制代碼
       public static class TransExpV2<TIn, TOut>
          {
      
              private static readonly Func<TIn, TOut> cache = GetFunc();
              private static Func<TIn, TOut> GetFunc()
              {
                  ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
                  List<MemberBinding> memberBindingList = new List<MemberBinding>();
      
                  foreach (var item in typeof(TOut).GetProperties())
                  {
               if (!item.CanWrite)
                    continue;
      MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name)); MemberBinding memberBinding = Expression.Bind(item, property); memberBindingList.Add(memberBinding); } MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray()); Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[] { parameterExpression }); return lambda.Compile(); } public static TOut Trans(TIn tIn) { return cache(tIn); } }
      復(fù)制代碼
      復(fù)制代碼

       

      調(diào)用:StudentSecond ss= TransExpV2<Student, StudentSecond>.Trans(s);

      調(diào)用一百萬次耗時:107毫秒

      耗時遠遠的小于使用automapper的338毫秒。 

       

      4、總結(jié)

      從以上的測試和分析可以很容易得出,用表達式樹是可以達到效率書寫方式二者兼?zhèn)涞姆椒?strong>之一,總之比傳統(tǒng)的序列化和反射更加優(yōu)秀。

      最后望對各位有所幫助,本文原創(chuàng),歡迎拍磚和推薦。  

       

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多