.net C#类型之间相互转换
遇到问题描述
在开发过程中经常遇到两个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; } }