AutoMapper MapHelper
本文提供两种mapper方式
- 反射
- 表达式(性能比反射更高)
测试运行一百万次映射,硬编码大概在15毫秒,反射560毫秒,表达式150毫秒
public static class MapperHelper
{
/// <summary>
/// 将数据映射到指定的对象中
/// </summary>
/// <typeparam name="TIn"></typeparam>
/// <typeparam name="TOut"></typeparam>
/// <param name="obj"></param>
/// <param name="outObj"></param>
/// <param name="ignorDesc"></param>
/// <returns></returns>
public static TOut AutoMap<TIn, TOut>(TIn obj, TOut outObj, bool ignorDesc = true) where TOut : new()
{
return AutoMap(obj, ignorDesc, outObj);
}
/// <summary>
///
/// </summary>
/// <typeparam name="TIn"></typeparam>
/// <typeparam name="TOut"></typeparam>
/// <param name="obj"></param>
/// <param name="ignorDesc">忽略description描述信息</param>
/// <returns></returns>
public static TOut AutoMap<TIn, TOut>(TIn obj, bool ignorDesc = true) where TOut : new()
{
TOut result = new TOut();
return AutoMap(obj, ignorDesc, result);
}
private static TOut AutoMap<TIn, TOut>(TIn obj, bool ignorDesc, TOut result) where TOut : new()
{
System.Reflection.PropertyInfo[] properties = obj.GetType().GetProperties();
//存储源对象属性
Dictionary<string, PropertyInfo> propertiesDic = new Dictionary<string, PropertyInfo>();
foreach (System.Reflection.PropertyInfo item in properties)
{
var hasSameKey = properties.Count(f => f.Name == item.Name) > 1;
var isHideBySig = item.GetGetMethod().IsHideBySig;
//避免因为子类添加new修饰符符导致出现重名字段
if (hasSameKey && isHideBySig && item.DeclaringType != obj.GetType())
{
continue;
}
propertiesDic.Add(item.Name, item);
}
System.Reflection.PropertyInfo[] resultProperties = result.GetType().GetProperties();
foreach (System.Reflection.PropertyInfo j in resultProperties)
{
try
{
////自定义属性处理别名
DescriptionAttribute desc = (DescriptionAttribute)j.GetCustomAttributes(false).FirstOrDefault(f => f.GetType() == typeof(DescriptionAttribute));
if (desc != null && !ignorDesc)
{
string desName = desc.Description;
if (propertiesDic.ContainsKey(desName))
{
j.SetValue(result, propertiesDic[desName].GetValue(obj));
continue;
}
}
else
{
if (propertiesDic.ContainsKey(j.Name))
{
j.SetValue(result, propertiesDic[j.Name].GetValue(obj));
}
}
}
catch (Exception)
{
try
{
j.SetValue(result, Activator.CreateInstance(j.PropertyType));
}
catch (Exception)
{
Console.WriteLine("转换前后类型不一致");
}
}
}
return result;
}
public static IList<TOut> AutoMap<TIn, TOut>(this List<TIn> list, bool ignorDesc = true) where TOut : new()
{
List<TOut> result = new List<TOut>();
foreach (TIn item in list)
{
try
{
result.Add(AutoMap<TIn, TOut>(item, ignorDesc));
}
catch (Exception e)
{
throw e;
}
}
return result;
}
public static object AutoMapByType(object obj, Type outType, bool ignorDesc = false)
{
object result = Activator.CreateInstance(outType);
System.Reflection.PropertyInfo[] properties = obj.GetType().GetProperties();
//存储源对象属性
Dictionary<string, PropertyInfo> propertiesDic = new Dictionary<string, PropertyInfo>();
foreach (System.Reflection.PropertyInfo item in properties)
{
propertiesDic.Add(item.Name, item);
}
System.Reflection.PropertyInfo[] resultProperties = outType.GetProperties();
foreach (System.Reflection.PropertyInfo j in resultProperties)
{
try
{
////自定义属性处理别名
DescriptionAttribute desc = (DescriptionAttribute)j.GetCustomAttributes(false).FirstOrDefault(f => f.GetType() == typeof(DescriptionAttribute));
if (desc != null && !ignorDesc)
{
string desName = desc.Description;
if (propertiesDic.ContainsKey(desName))
{
j.SetValue(result, propertiesDic[desName].GetValue(obj));
continue;
}
}
else
{
if (propertiesDic.ContainsKey(j.Name))
{
j.SetValue(result, propertiesDic[j.Name].GetValue(obj));
}
}
}
catch (Exception)
{
try
{
j.SetValue(result, Activator.CreateInstance(j.PropertyType));
}
catch (Exception)
{
Console.WriteLine("转换前后类型不一致");
}
}
}
return result;
}
}
上面使用的是反射的模式,性能虽然比反序列化要高,可还是比硬编码低很多,可以采用表达式的方法来提高性能(性能接近硬编码-约比反射模式性能高30倍)
public static class ExpressionMapper
{
/// <summary>
/// 字典缓存,保存的是委托,委托内部是转换的动作
/// </summary>
private static Dictionary<string, object> _Dic = new Dictionary<string, object>();
/// <summary>
/// Expression动态拼接+普通缓存
/// </summary>
/// <typeparam name="TIn"></typeparam>
/// <typeparam name="TOut"></typeparam>
/// <param name="tIn"></param>
/// <returns></returns>
public static TOut Trans<TIn, TOut>(TIn tIn)
{
string key = $"funckey_{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);
}
foreach (var item in typeof(TOut).GetFields())
{
MemberExpression property = Expression.Field(parameterExpression, typeof(TIn).GetField(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]).Invoke(tIn);
}
}
调用方法
var resultA = ExpressionMapper.Trans<MapperModelB, MapperModelA>(datab);
[参考]
C# Expression详解
留待后查,同时方便他人
联系我:renhanlinbsl@163.com
联系我:renhanlinbsl@163.com