国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > C# > 正文

C# 快速高效率復(fù)制對(duì)象(表達(dá)式樹)

2019-10-29 21:11:47
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

1、需求

在代碼中經(jīng)常會(huì)遇到需要把對(duì)象復(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; }  }

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

我們需要給新的Student賦值

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

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

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

2、解決辦法

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

2.1、反射

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

private static TOut TransReflection<TIn, TOut>(TIn tIn)  {   TOut tOut = Activator.CreateInstance<TOut>();   foreach (var itemOut in tOut.GetType().GetProperties())   {    var itemIn = tIn.GetType().GetProperties().Where(i => i.Name == itemOut.Name).FirstOrDefault();    if (itemIn != null)    {     itemOut.SetValue(tOut, itemIn.GetValue(tIn));    }   }   return tOut;  }

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

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

2.2、序列化

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

調(diào)用:

StudentSecond ss= JsonConvert.DeserializeObject<StudentSecond>(JsonConvert.SerializeObject(s));

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

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

3、表達(dá)式樹

3.1、簡(jiǎn)介

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

也就是說(shuō)復(fù)制對(duì)象也可以用表達(dá)式樹的方式。

  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);

這樣的方式我們可以達(dá)到同樣的效果。

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

3.2、分析代碼

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

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);

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

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

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

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

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

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())    {     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);  }

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

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

4、總結(jié)

從以上的測(cè)試和分析可以很容易得出,用表達(dá)式樹是可以達(dá)到效率與書寫方式二者兼?zhèn)涞姆椒ㄖ唬傊葌鹘y(tǒng)的序列化和反射更加優(yōu)秀。

以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,同時(shí)也希望多多支持VEVB武林網(wǎng)!


注:相關(guān)教程知識(shí)閱讀請(qǐng)移步到c#教程頻道。
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 神木县| 洮南市| 姜堰市| 廊坊市| 墨江| 若羌县| 南城县| 偏关县| 通海县| 喀喇沁旗| 潞城市| 年辖:市辖区| 卢氏县| 绥芬河市| 仪征市| 汉源县| 台中县| 治县。| 布拖县| 筠连县| 乌鲁木齐市| 清水县| 喜德县| 西平县| 禹城市| 肥东县| 常熟市| 和龙市| 石林| 平顶山市| 宣武区| 阿坝| 时尚| 富平县| 竹山县| 隆林| 睢宁县| 新安县| 清镇市| 富平县| 堆龙德庆县|