遇到問題描述
在開發過程中經常遇到兩個Model長得一模一樣,但就是訂單的類型不一樣。常用到的框架結構是MVC中:業務邏輯層的Model與View層的Model的裝換。每層定義屬於自己Model,實現層與層之間的解耦。最簡單小白的方法就是在每個要賦值的地方 new 一個 object, 之后實現一個一個賦值。
嘗試寫公用的方法
A.通過反射獲得原object中的內容,之后創建一個泛型的目標object對象,之后進行一一復制; 自己實現的實例如下:
最簡單的只能實現一層轉換 不支持list 結合的轉換
/// <summary>
///OTO object到object 的轉換
/// </summary>
/// <typeparam name="T1">目標類型 </typeparam>
/// <param name="sourceObject">要裝換的object</param>
/// <param name="excludedProperties">裝換的object中不需要裝換的屬性名稱</param>
/// <returns></returns>
public static T1 MapProperties<T1>(object sourceObject, string[] excludeProperties = null) where T1 : class
{
var targetType = typeof(T1);
var targetObject = Activator.CreateInstance(targetType);
foreach (var sourceProperty in sourceObject.GetType().GetProperties())
{
if (excludeProperties != null && excludeProperties.Contains(sourceProperty.Name))
{
continue;
}
var targetObjectProperty = targetObject.GetType().GetProperty(sourceProperty.Name);
if (targetObjectProperty != null && sourceProperty.PropertyType == targetObjectProperty.PropertyType)
{
var sourceValue = sourceProperty.GetValue(sourceObject);
targetObjectProperty.SetValue(targetObject, sourceValue);
}
}
return (T1)targetObject;
}
能實現一層與簡單2層的(2層中引用對象的類型一致),不支持list集合的方式
/// <summary>
/// 轉換兩個不同類型但是成員相同的對象,包含私有字段的賦值
/// </summary>
/// <typeparam name="T">目標對象</typeparam>
/// <param name="source">待轉換對象</param>
/// <returns></returns>
public static T CopySameFieldsObject<T>(Object source) where T: class
{
Type srcT = source.GetType();
Type destT = typeof(T);
// 構造一個要轉換對象實例
Object instance = destT.InvokeMember("", BindingFlags.CreateInstance, null, null, null);
// 這里指定搜索所有公開和非公開的字段
FieldInfo[] srcFields = srcT.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
// 將源頭對象的每個字段的值分別賦值到轉換對象里,因為假定字段都一樣,這里就不做容錯處理了
foreach (FieldInfo field in srcFields)
{
destT.GetField(field.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).
SetValue(instance, field.GetValue(source));
}
return (T)instance;
}

1 public class PagerModel 2 { 3 public PagerModel() 4 { 5 TastPageModel = new TastPageModel(); 6 TastPageModels = new List<TastPageModel>(); 7 } 8 9 public int CurrentPage { get; set;} 10 public int PageSize { get; set;} 11 public int Total { get; set;} 12 public int TotalPage 13 { 14 get 15 { 16 return (this.Total + this.PageSize - 1) / this.PageSize; 17 } 18 } 19 //簡單序列化對象 20 public override string ToString() 21 { 22 return JsonConvert.SerializeObject(this); 23 } 24 public TastPageModel TastPageModel; 25 26 public List<TastPageModel> TastPageModels; 27 28 } 29 30 public class TastPageModel 31 { 32 public string TastString { get; set;} 33 34 public Object ObjTry { get; set;} 35 } 36 37 public class TastPageModel2 38 { 39 public string TastString { get; set; } 40 41 public Object ObjTry { get; set; } 42 } 43 public class PagerModel2 44 { 45 public PagerModel2() { 46 TastPageModel = new TastPageModel(); 47 TastPageModels = new List<TastPageModel2>(); 48 } 49 50 public TastPageModel TastPageModel; 51 public List<TastPageModel2> TastPageModels; 52 public int CurrentPage { get; set; } 53 public int PageSize { get; set; } 54 public int Total { get; set; } 55 public int TotalPage 56 { 57 get 58 { 59 return (this.Total + this.PageSize - 1) / this.PageSize; 60 } 61 } 62 //簡單序列化對象 63 public override string ToString() 64 { 65 return JsonConvert.SerializeObject(this); 66 } 67 } 68 69 70 ----運行代碼 71 72 public static void MapTest() 73 { 74 var model = new PagerModel() 75 { 76 Total=100, 77 PageSize = 20 78 }; 79 model.TastPageModel.TastString = "aaaaa"; 80 81 82 model.TastPageModels.Add(new TastPageModel() { 83 TastString = "bbbb", 84 ObjTry = new { 85 oa="ccccc", 86 } 87 }); 88 89 var model3 = ModelMap.CopySameFieldsObject<PagerModel2>(model); 90 //var model2 = ModelMap.MapProperties<PagerModel2>(model); 91 }
試驗結果就是:
(1)轉換對象層級是一級的是沒有問題;
(2)二級的類是一致的也能裝換(可以把PagerModel2 中成員 public List<TastPageModel2> TastPageModels; 改為 public List<TastPageModel> TastPageModels);
(3)二級中類成員是一致的,但是屬於不同的類型就不行了,上邊實例中充分說明了這一點。多級的就更不用說了。
結論:
這種方法是可以實現我們的目的,但是可能要處理,考慮的點比較多,容易形成漏洞。就是說輪子是這樣造的,但是我們不用在重新去研究具體怎么造的。
B:最簡單暴力的處理方式,管你里面有什么東西,直接Json序列化,再反序列化,使用這種方式的人,大部分是不願意引用第三方插件。
其中需要安裝 System.Security.Permissions包
/// <summary>
/// 將object對象轉換為實體對象序列化與反序列化的方式
/// </summary>
/// <typeparam name="T">實體對象類名</typeparam>
/// <param name="asObject">object對象</param>
/// <returns></returns>
public static T ConvertObject<T>(object sourceObject) where T : class
{
if (sourceObject == null)
{
return null;
}
//將object對象轉換為json字符
var json = Newtonsoft.Json.JsonConvert.SerializeObject(sourceObject);
//將json字符轉換為實體對象
var targetObject = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(json);
return targetObject;
}
試驗結果: 完美解決遇到的大部分問題,為什么是大部分呢? 執行的效率怎么樣?有沒有字節的限制? 需要我們去驗證。
C:找專業的第三方插件,現在常用到的就是AutoMapper,可以在Nuget包中添加,本次試驗用的版本是(8.1.1) 48M大小。
我們采用這種拿來主義的話,就探討一下他的執行效率,可以反編譯去查看一下里面的實現方式。自己可以學習一下。其中的反編譯工具用的是ILSpy 。 AutoMapper 中的內容比較多,你編程中用到的所有裝換問題都能實現
AutoMapper Github上源代碼: AutoMapper https://github.com/AutoMapper/AutoMapper
ILSpy Github上源代碼(以及下載地址):https://github.com/icsharpcode/ILSpy#ilspy------- (可Download: latest release 下載最新版本的zip包,直接exe打開運行)
AutoMapper 官網使用手冊: http://docs.automapper.org/en/latest/Dependency-injection.html
AutoMapper 博客大神們分享8分鍾學會使用automapper: https://www.cnblogs.com/ZaraNet/p/10000311.html
博客園中系列文檔 :https://www.cnblogs.com/xishuai/p/3700052.html
CSDN上系列文檔: https://blog.csdn.net/WuLex/article/details/78646260
用戶比較詳細的測試用例在github中 : https://github.com/AutoMapper/AutoMapper/blob/master/src/IntegrationTests/ChildClassTests.cs
在這里我們自己寫的實例在這里給了一個比較常用的類型轉換的實例 Expression-Translation ,這兩個實例是立馬都能上手用的裝換,其他比較復雜,就需要自己去找文檔了,

//4.AutoMapper 中使用的情況 //現在一般是使用依賴注入的方式 Mapper.Initialize(cfg => { cfg.CreateMap<PagerModel, PagerModel2>(); cfg.CreateMap<TastPageModel, TastPageModel2>(); }); var model6 = AutoMapper.Mapper.Map<PagerModel2>(model); //5.AutoMapper 中List 的轉換 var model7 = AutoMapper.Mapper.Map<List<PagerModel2>>(modellist);
B,C兩種執行效率的比對:

public static void MapTest() { var model = new PagerModel() { Total=100, PageSize = 20 }; model.TastPageModel.TastString = "aaaaa"; model.TastPageModels.Add(new TastPageModel() { TastString = "bbbb", ObjTry = new { oa="ccccc", } }); //1.自己寫的裝換的方法 只能裝換類型完全一樣的,不一樣的會出錯 //var model3 = ModelMap.CopySameFieldsObject<PagerModel2>(model); //2.序列化與反序列化的試驗 //var model4 = ModelMap.ConvertObject<PagerModel2>(model); //3.探討序列化中是否受字節的限制,以及性能上的優化狀況 var modellist = new List<PagerModel>(); for (int i = 0; i < 1000000; i++) { var modelNew = new PagerModel() { Total = 100, PageSize = 20 }; modelNew.TastPageModel.TastString = "aaaaa"; modelNew.TastPageModels.Add(new TastPageModel() { TastString = "bbbb", ObjTry = new { oa = "ccccc", } }); modellist.Add(modelNew); } var stopWatch = new Stopwatch(); stopWatch.Start(); var model5 = ModelMap.ConvertObject<List<PagerModel2>>(modellist); stopWatch.Stop(); Console.WriteLine($"序列化與反序列化執行時間:{stopWatch.ElapsedMilliseconds}" ); //4.AutoMapper 中使用的情況 //現在一般是使用依賴注入的方式 Mapper.Initialize(cfg => { cfg.CreateMap<PagerModel, PagerModel2>(); cfg.CreateMap<TastPageModel, TastPageModel2>(); }); var model6 = AutoMapper.Mapper.Map<PagerModel2>(model); //5.AutoMapper 中List 的轉換 //執行時間的統計 stopWatch.Reset(); stopWatch.Start(); var model7 = AutoMapper.Mapper.Map<List<PagerModel2>>(modellist); stopWatch.Stop(); Console.WriteLine($"AutoMapper中的轉換時間:{stopWatch.ElapsedMilliseconds}"); }
執行結果:
由此可見序列化與分序列化轉換的速度還是太慢了.
D:特殊實例演示

public void JsonDeserialize() { var targetObject1 = new JApiGetMode<object>(); var targetObject2 = new JApiGetMode<ShopCompanyInfo>(); //動態對象 var targetObject3 = new { Data = new ShopCompanyInfo()}; //要轉換的Json var sourceJsonObject = @"{'code':10000,'message':'操作成功','data':{'shopId':30447,'shopName':'汽保 - 05','companyName':'上海皮卡丘有限公司','companyId':'5711'},'success':true}"; //轉換對象泛型為object var desrObj1 = JsonConvert.DeserializeAnonymousType(sourceJsonObject, targetObject1); //返回的Data對象為 {{ "shopId": 30447,"shopName": "汽保 - 05","companyName": "上海皮卡丘有限公司","companyId": "5711"}} //三種可用的轉換方式 var desrObj2 = JsonConvert.DeserializeAnonymousType(sourceJsonObject, targetObject2); //訪問方式 desrObj2.Data.ShopId為30447 var desrObj3 = JsonConvert.DeserializeAnonymousType(sourceJsonObject, targetObject3); //訪問方式 desrObj3.Data.ShopId為30447 //常用的方法 var desrObj4 = JsonConvert.DeserializeObject<JApiGetMode<ShopCompanyInfo>>(sourceJsonObject); //訪問方式 desrObj4.Data.ShopId為30447 // var sourceJsonObject2 = @"{'code':10000,'message':'操作成功','data':[{'shopId':30447,'shopName':'汽保 - 05'},{'shopId':30448,'shopName':'汽保 - 06'}],'success':true}"; //只解析Json中的一部分 var targetObject5 = new { Data = new List<ShopCompanyInfo>() }; var desrObj5 = JsonConvert.DeserializeAnonymousType(sourceJsonObject2, targetObject5); //可以把data 中的轉為ShopCompanyInfo 的值對象 var desrObj6 = JsonConvert.DeserializeObject<JApiGetModes<ShopCompanyInfo>>(sourceJsonObject2); //可以 } public class ShopCompanyInfo { public int ShopId { get; set; } public string ShopName { get; set; } public string CompanyId { get; set; } public string CompanyName { get; set; } } public class JApiGetMode<T> where T : new() { public int Code { get; set; } public string Message { get; set; } public T Data { get; set; } public bool Success { get; set; } } public class JApiGetModes<T> { public JApiGetModes() { Data = new List<T>(); } public int Code { get; set; } public string Message { get; set; } public List<T> Data { get; set; } public bool Success { get; set; } }