c#表达式树最完善的表达式树Expression.Dynamic的玩法
Posted 四处观察
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c#表达式树最完善的表达式树Expression.Dynamic的玩法相关的知识,希望对你有一定的参考价值。
引言
在我第一次写博客的时候,写的第一篇文章,就是关于表达式树的,链接:https://www.cnblogs.com/1996-Chinese-Chen/p/14987967.html,其中,当时一直没有研究Expression.Dynamic的使用方法(因为网上找不到资料),就了解到是程序运行时动态去构建表达式树,举个例子,例如我们需要在我们的查询条件中去构建他是等于或者不等于,这个时候,虽然我们可以定义等于或者不定于 的BinaryExpression,然后在代码中通过switch去进行判断,使用的是Equal还是NotEqual,这中间还需要我们自己去写一个switch,如果使用了Dynamic的方法,我们就只需要找到对应的ExpressionType然后传入创建Binder的方法中,在调用Dynamic方法就可以动态的实现,各种判断操作,或者其他的调用方法,灵活度比switch更高,接下来,我们就看看如何使用Expression.Dynamic方法来实现各种操作吧,一下所有代码操作需要引入Microsoft.CSharp.RuntimeBinder,nuget搜索Microsoft.CSharp即可。方便测试,我新建了一个Test的类,下面会用到
public class Test private List<string> Strings = new List<string>(); public event Action TestEvent; public Test(int a,int b) A = a; B = b; Strings.Add("1"); Strings.Add("2"); Strings.Add("3"); public string this[int Index] get=> Strings[Index]; set=> Strings[Index]=value; public int A get; set; public int B get; set; public int Add() return A+B; public static int AddOne() return 15;
二元运算
下面的代码实现一个二元运算,首先Dynamic方法是需要CallBinder参数的,而对应的实现有如下的Binder,我们首先需要去创建对应的Binder,二元运算就使用BinaryOperation方法创建,CSharpBinderFlags是一个枚举类型,它用于指定动态绑定操作的行为,里面可以定义在动态绑定的时候需要执行的一些特殊操作,例如,运算应该在已经检查的上下文中运行,或者使用Invoke等需要使用的一些特殊操作,或者转换的时候等等。第二个参数是ExpressionType,标明我们是那一个二元运算,第三个是当前代码运行的主体类型 that indicates where this operation is used.即这个指示了这个操作被用在哪些地方。第三个是一个CSharpArgumentInfo集合,是我们创建这个站点的时候需要使用的参数数量,如果是调用方法的时候,或者获取实例属性的时候,第一个参数是为实例参数,UseCompileTimeType类型是编译期间确定类型,其中还有IsStaticType,IsRef,IsOUt等各种,供我们使用。
然后我们创建一个dynamic的Expression,传入binder,返回类型是object,然后传入需要计算的两个参数10和1,最后得到委托,运行委托即可。
CallSiteBinder binder = Binder.BinaryOperation( CSharpBinderFlags.None, ExpressionType.LeftShift, typeof(Program), new[] CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) ); var dynamic = Expression.Dynamic(binder, typeof(object), Expression.Constant(10), Expression.Constant(1)); Func<int> func = Expression.Lambda<Func<int>>(Expression.Convert(dynamic, typeof(int))).Compile(); Console.WriteLine(func());
创建实例
从上面的Test类看到,我们定义了两个入参,可能有的人会问了为什么入参是两个Binder为什么定义了三个呢,这是因为,创建性的Binder在创建的时候 参数第一个必须是类型参数,所以此处第一个参数必须是Test的type,然后后面是Static类型的参数,
最后一个参数就是3,调用Dynamic,第二个为返回类型的参数,然后传入对应的参数即可创建对象。
static int A = 5;
var constructorBinder = Binder.InvokeConstructor(CSharpBinderFlags.None, typeof(Program), new[] CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType,null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType,null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType,null) ); var createInstance = Expression.Dynamic(constructorBinder, typeof(Test), Expression.Constant(typeof(Test)), Expression.Constant(A), Expression.Constant(3)); var instance = Expression.Lambda<Func<Test>>(createInstance).Compile()();
调用方法
实例方法
实例方法,使用InvokeMember,第二个参数是调用的方法名称,第三个参数是参数类型,由于我没有定义参数所以为null,然后实例方法我们需要定义一个实例参数,在CSharpArgumentInfo定义,然后调用Dynamic,返回类型必须是Object,因为这块扯犊子的是他直接写死的,如果需要转只有自己到表达式树那块Convert转,调用然后生成委托,返回结果。
var invokeBinder = Binder.InvokeMember( CSharpBinderFlags.None, "Add", null, typeof(Program), new[] CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) ); var invokeDynamic=Expression.Dynamic(invokeBinder, typeof(object),Expression.Constant(instance)); var returnVal = Expression.Lambda<Func<object>>(invokeDynamic).Compile()(); Console.WriteLine(returnVal);
静态方法
大体上没有区别,在参数类型需要标记为StaticType。传入的参数不再是实例,而是静态方法所属的类型下,可以看到,返回类型必须是Object,我自己在最后Convert了,源码中的Binder默认写死Object
var invokeStaticBinder = Binder.InvokeMember( CSharpBinderFlags.None, "AddOne", null, typeof(Test), new[] CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null) ); var invokeStaticDynamic = Expression.Dynamic(invokeStaticBinder, typeof(object),Expression.Constant(typeof(Test))); var Val = Expression.Lambda<Func<int>>(Expression.Convert(invokeStaticDynamic,typeof(int))).Compile()(); Console.WriteLine(Val);
转换
将int转换为Object类型。
var bindConvert = Binder.Convert(CSharpBinderFlags.None,typeof(object),typeof(Program)); var expressConvert = Expression.Dynamic(bindConvert,typeof(object),Expression.Constant(A)); var funcVal=Expression.Lambda<Func<object>>(expressConvert).Compile()();
Set Get属性
下面是Set,第二个参数是设置的属性名称,参数类型是实例,以及设置的属性值,最后生成委托,然后调用即可。
var bindSet = Binder.SetMember(CSharpBinderFlags.None, "A", typeof(Program), new[] CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) ); var setExpress = Expression.Dynamic(bindSet,typeof(void), Expression.Constant(instance),Expression.Constant(100)); var action = Expression.Lambda<Action>(setExpress).Compile(); action();
然后是Get,参数是实例的,然后返回就行了。
var bindGet = Binder.GetMember(CSharpBinderFlags.None, "A", typeof(Program), new[] CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) ); var getExpress = Expression.Dynamic(bindGet, typeof(object), Expression.Constant(instance)); var getFunc= Expression.Lambda<Func<object>>(getExpress).Compile()(); Console.WriteLine(getFunc);
一元运算
一元运算的ExpressionType,参数的定义,Binder和表达式树绑定,生成委托。
var NegateBinder = Binder.UnaryOperation(CSharpBinderFlags.None,ExpressionType.Negate,typeof(Program),new[] CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) ); var NegateExpress = Expression.Dynamic(NegateBinder, typeof(object), Expression.Constant(10)); var NegateVal = Expression.Lambda<Func<object>>(NegateExpress).Compile()();
Get Set Index
先Set,第一个参数自变量,第二个为索引,第三个是具体的值,然后表达式树和Binder绑定,生成委托,调用,即可,可以看到上面Test我们定义了一个Index的。
var setIndex = Binder.SetIndex(CSharpBinderFlags.None, typeof(Test), new[] CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) ); var setIndexExpress = Expression.Dynamic(setIndex,typeof(void),Expression.Constant(instance),Expression.Constant(1),Expression.Constant("cxd")); var SetIndexaction = Expression.Lambda<Action>(setIndexExpress).Compile(); SetIndexaction();
然后是get,自变量,索引,生成委托,返回索引的值。
var getIndex= Binder.GetIndex(CSharpBinderFlags.None, typeof(Program), new[] CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) ); var getIndexExpress = Expression.Dynamic(getIndex, typeof(object), Expression.Constant(instance), Expression.Constant(0)); var getIndexaction = Expression.Lambda<Func<object>>(getIndexExpress).Compile()();
IsEvent
判断属性是不是事件类型的,第二个是属性名称,返回值是bool。
var isevent = Binder.IsEvent(CSharpBinderFlags.None, "TestEvent", typeof(Program));//换成非Event就不行 var iseventExpress = Expression.Dynamic(isevent,typeof(bool),Expression.Constant(instance)); var res=Expression.Lambda<Func<bool>>(iseventExpress).Compile()(); Console.WriteLine(res);
Invoke
这个是用来调用委托的,我们定义一个Func的委托,可惜的是,返回值还是只能是object,然后参数参数,然后调用委托,就返回了111。
var actions= new Func<object>(()=>111); var invokeOtherBinder = Binder.Invoke(CSharpBinderFlags.None,typeof(Program),new[] CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), ); var expression = Expression.Dynamic(invokeOtherBinder, typeof(object),Expression.Constant(actions)); var ra=Expression.Lambda<Func<object>>(expression).Compile()();
结尾
下次再见,欢迎大家加群讨论,我是四川观察,感谢各位看官的支持,谢谢大家,咱们下次再见。
C# 高性能对象映射(表达式树实现)
来源:costyuan
cnblogs.com/castyuan/p/9324088.html
前言
上篇简单实现了对象映射,针对数组,集合,嵌套类并没有给出实现,这一篇继续完善细节。
开源对象映射类库映射分析
1.AutoMapper
实现原理:主要通过表达式树Api 实现对象映射
优点: .net功能最全的对象映射类库。
缺点:当出现复杂类型和嵌套类型时性能直线下降,甚至不如序列化快
2.TinyMapper
实现原理:主要通过Emit 实现对象映射
优点:速度非常快。在处理复杂类型和嵌套类型性能也很好
缺点:相对AutoMapper功能上少一些,Emit的实现方案,在代码阅读和调试上相对比较麻烦,而表达式树直接观察 DebugView中生成的代码结构便可知道问题所在
3. 本文的对象映射库
针对AutoMapper 处理复杂类型和嵌套类型时性能非常差的情况,自己实现一个表达式树版的高性能方案
此篇记录下实现对象映射库的过程
构造测试类
public class TestA
{
public int Id { get; set; }
public string Name { get; set; }
public TestC TestClass { get; set; }
public IEnumerable<TestC> TestLists { get; set; }
}
public class TestB
{
public int Id { get; set; }
public string Name { get; set; }
public TestD TestClass { get; set; }
public TestD[] TestLists { get; set; }
}
public class TestC
{
public int Id { get; set; }
public string Name { get; set; }
public TestC SelfClass { get; set; }
}
public class TestD
{
public int Id { get; set; }
public string Name { get; set; }
public TestD SelfClass { get; set; }
}
1、初步实现
利用表达式树给属性赋值 利用 Expresstion.New构造 var b=new B{};
private static Func<TSource, TTarget> GetMap<TSource, TTarget>()
{
var sourceType = typeof(TSource);
var targetType = typeof(TTarget);
//构造 p=>
var parameterExpression = Expression.Parameter(sourceType, "p");
//构造 p=>new TTarget{ Id=p.Id,Name=p.Name };
var memberBindingList = new List<MemberBinding>();
foreach (var sourceItem in sourceType.GetProperties())
{
var targetItem = targetType.GetProperty(sourceItem.Name);
if (targetItem == null || sourceItem.PropertyType != targetItem.PropertyType)
continue;
var property = Expression.Property(parameterExpression, sourceItem);
var memberBinding = Expression.Bind(targetItem, property);
memberBindingList.Add(memberBinding);
}
var memberInitExpression = Expression.MemberInit(Expression.New(targetType), memberBindingList);
var lambda = Expression.Lambda<Func<TSource, TTarget>>(memberInitExpression, parameterExpression );
Console.WriteLine(lambda);
return lambda.Compile();
}
调用如下
class Program
{
static void Main(string[] args)
{
var testA = new TestA { Id = 1, Name = "张三" };
var func = Map<TestA, TestB>();
TestB testB = func(testA);
Console.WriteLine($"testB.Id={testB.Id},testB.Name={testB.Name}");
Console.ReadLine();
}
}
输出结果
总结:此方法需要调用前需要手动编译下,然后再调用委托没有缓存委托,相对麻烦。
2、缓存实现
利用静态泛型类缓存泛型委托
public class DataMapper<TSource, TTarget>
{
private static Func<TSource, TTarget> MapFunc { get; set; }
public static TTarget Map(TSource source)
{
if (MapFunc == null)
MapFunc = GetMap();//方法在上边
return MapFunc(source);
}
}
调用方法
static void Main(string[] args)
{
var testA = new TestA { Id = 1, Name = "张三" };
TestB testB = DataMapper<TestA, TestB>.Map(testA);//委托不存在时自动生成,存在时调用静态缓存
Console.WriteLine($"testB.Id={testB.Id},testB.Name={testB.Name}");
Console.ReadLine();
}
输出结果
总结:引入静态泛型类能解决泛型委托缓存提高性能,但是有两个问题 1.当传入参数为null时 则会抛出空引用异常 2.出现复杂类型上述方法便不能满足了
3、解决参数为空值和复杂类型的问题
首先先用常规代码实现下带有复杂类型赋值的情况
public TestB GetTestB(TestA testA)
{
TestB testB;
if (testA != null)
{
testB = new TestB();
testB.Id = testA.Id;
testB.Name = testA.Name;
if (testA.TestClass != null)
{
testB.TestClass = new TestD();
testB.TestClass.Id = testA.TestClass.Id;
testB.TestClass.Name = testA.TestClass.Name;
}
}
else
{
testB = null;
}
return testB;
}
将上面的代码翻译成表达式树
private static Func<TSource, TTarget> GetMap()
{
var sourceType = typeof(TSource);
var targetType = typeof(TTarget);
//Func委托传入变量
var parameter = Expression.Parameter(sourceType);
//声明一个返回值变量
var variable = Expression.Variable(targetType);
//创建一个if条件表达式
var test = Expression.NotEqual(parameter, Expression.Constant(null, sourceType));// p==null;
var ifTrue = Expression.Block(GetExpression(parameter, variable, sourceType, targetType));
var IfThen = Expression.IfThen(test, ifTrue);
//构造代码块
var block = Expression.Block(new[] { variable }, parameter, IfThen, variable);
var lambda = Expression.Lambda<Func<TSource, TTarget>>(block, parameter);
return lambda.Compile();
}
private static List<Expression> GetExpression(Expression parameter, Expression variable, Type sourceType, Type targetType)
{
//创建一个表达式集合
var expressions = new List<Expression>();
expressions.Add(Expression.Assign(variable, Expression.MemberInit(Expression.New(targetType))));
foreach (var targetItem in targetType.GetProperties().Where(x => x.PropertyType.IsPublic && x.CanWrite))
{
var sourceItem = sourceType.GetProperty(targetItem.Name);
//判断实体的读写权限
if (sourceItem == null || !sourceItem.CanRead || sourceItem.PropertyType.IsNotPublic)
continue;
var sourceProperty = Expression.Property(parameter, sourceItem);
var targetProperty = Expression.Property(variable, targetItem);
//判断都是class 且类型不相同时
if (targetItem.PropertyType.IsClass && sourceItem.PropertyType.IsClass && targetItem.PropertyType != sourceItem.PropertyType)
{
if (targetItem.PropertyType != targetType)//不处理嵌套循环的情况
{
//由于类型是class 所以默认值是null
var testItem = Expression.NotEqual(sourceProperty, Expression.Constant(null, sourceItem.PropertyType));
var itemExpressions = GetExpression(sourceProperty, targetProperty, sourceItem.PropertyType, targetItem.PropertyType);
var ifTrueItem = Expression.Block(itemExpressions);
var IfThenItem = Expression.IfThen(testItem, ifTrueItem);
expressions.Add(IfThenItem);
continue;
}
}
//目标值类型时 且两者类型不一致时跳过
if (targetItem.PropertyType != sourceItem.PropertyType)
continue;
expressions.Add(Expression.Assign(targetProperty, sourceProperty));
}
return expressions;
}
总结:此方案,运用 Expression.IfThen(testItem, ifTrueItem) 判断空值问题,通过递归调用 GetExpression()方法,处理复杂类型。
但是。。。针对嵌套类仍然不能解决。因为表达式树是在实际调用方法之前就生成的,在没有实际的
参数值传入之前,生成的表达式是不知道有多少层级的。
有个比较low的方案是,预先设定嵌套层级为10层,然后生成一个有10层 if(P!=null) 的判断。如果传入的参数层级超过10层了呢,就得手动调整生成的树,此方案也否决。
最后得出的结论只能在表达式中动态调用方法。
4、最终版本
通过动态调用方法解决嵌套类,代码如下
using System.Collections;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using static System.Linq.Expressions.Expression;
public static class Mapper<TSource, TTarget> where TSource : class where TTarget : class
{
private static Func<TSource, TTarget> MapFunc { get; set; }
public static TTarget Map(TSource source)
{
if (MapFunc == null)
MapFunc = GetMap();
return MapFunc(source);
}
public static List<TTarget> MapList(IEnumerable<TSource> sources)
{
if (MapFunc == null)
MapFunc = GetMap();
var result = new List<TTarget>();
foreach (var item in sources)
{
result.Add(MapFunc(item));
}
return result;
}
private static Func<TSource, TTarget> GetMap()
{
var sourceType = typeof(TSource);
var targetType = typeof(TTarget);
//Func委托传入变量
var parameter = Parameter(sourceType, "p");
var memberBindings = new List<MemberBinding>();
var targetTypes = targetType.GetProperties().Where(x => x.PropertyType.IsPublic && x.CanWrite);
foreach (var targetItem in targetTypes)
{
var sourceItem = sourceType.GetProperty(targetItem.Name);
//判断实体的读写权限
if (sourceItem == null || !sourceItem.CanRead || sourceItem.PropertyType.IsNotPublic)
continue;
//标注NotMapped特性的属性忽略转换
if (sourceItem.GetCustomAttribute<NotMappedAttribute>() != null)
continue;
var sourceProperty = Property(parameter, sourceItem);
//当非值类型且类型不相同时
if (!sourceItem.PropertyType.IsValueType && sourceItem.PropertyType != targetItem.PropertyType)
{
//判断都是(非泛型)class
if (sourceItem.PropertyType.IsClass && targetItem.PropertyType.IsClass &&
!sourceItem.PropertyType.IsGenericType && !targetItem.PropertyType.IsGenericType)
{
var expression = GetClassExpression(sourceProperty, sourceItem.PropertyType, targetItem.PropertyType);
memberBindings.Add(Expression.Bind(targetItem, expression));
}
//集合数组类型的转换
if (typeof(IEnumerable).IsAssignableFrom(sourceItem.PropertyType) && typeof(IEnumerable).IsAssignableFrom(targetItem.PropertyType))
{
var expression = GetListExpression(sourceProperty, sourceItem.PropertyType, targetItem.PropertyType);
memberBindings.Add(Expression.Bind(targetItem, expression));
}
continue;
}
if (targetItem.PropertyType != sourceItem.PropertyType)
continue;
memberBindings.Add(Bind(targetItem, sourceProperty));
}
//创建一个if条件表达式
var test = NotEqual(parameter, Constant(null, sourceType));// p==null;
var ifTrue = MemberInit(New(targetType), memberBindings);
var condition = Condition(test, ifTrue, Constant(null, targetType));
var lambda = Lambda<Func<TSource, TTarget>>(condition, parameter);
return lambda.Compile();
}
/// <summary>
/// 类型是clas时赋值
/// </summary>
/// <param name="sourceProperty"></param>
/// <param name="targetProperty"></param>
/// <param name="sourceType"></param>
/// <param name="targetType"></param>
/// <returns></returns>
private static Expression GetClassExpression(Expression sourceProperty, Type sourceType, Type targetType)
{
//条件p.Item!=null
var testItem = NotEqual(sourceProperty, Constant(null, sourceType));
//构造回调 Mapper<TSource, TTarget>.Map()
var mapperType = typeof(Mapper<,>).MakeGenericType(sourceType, targetType);
var iftrue = Call(mapperType.GetMethod(nameof(Map), new[] { sourceType }), sourceProperty);
var conditionItem = Condition(testItem, iftrue, Constant(null, targetType));
return conditionItem;
}
/// <summary>
/// 类型为集合时赋值
/// </summary>
/// <param name="sourceProperty"></param>
/// <param name="targetProperty"></param>
/// <param name="sourceType"></param>
/// <param name="targetType"></param>
/// <returns></returns>
private static Expression GetListExpression(Expression sourceProperty, Type sourceType, Type targetType)
{
//条件p.Item!=null
var testItem = NotEqual(sourceProperty, Constant(null, sourceType));
//构造回调 Mapper<TSource, TTarget>.MapList()
var sourceArg = sourceType.IsArray ? sourceType.GetElementType() : sourceType.GetGenericArguments()[0];
var targetArg = targetType.IsArray ? targetType.GetElementType() : targetType.GetGenericArguments()[0];
var mapperType = typeof(Mapper<,>).MakeGenericType(sourceArg, targetArg);
var mapperExecMap = Call(mapperType.GetMethod(nameof(MapList), new[] { sourceType }), sourceProperty);
Expression iftrue;
if (targetType == mapperExecMap.Type)
{
iftrue = mapperExecMap;
}
else if (targetType.IsArray)//数组类型调用ToArray()方法
{
iftrue = Call(mapperExecMap, mapperExecMap.Type.GetMethod("ToArray"));
}
else if (typeof(IDictionary).IsAssignableFrom(targetType))
{
iftrue = Constant(null, targetType);//字典类型不转换
}
else
{
iftrue = Convert(mapperExecMap, targetType);
}
var conditionItem = Condition(testItem, iftrue, Constant(null, targetType));
return conditionItem;
}
}
输出的 表达式
格式化后
p => IIF((p != null),
new TestB()
{
Id = p.Id,
Name = p.Name,
TestClass = IIF(
(p.TestClass != null),
Map(p.TestClass),
null
),
TestLists = IIF(
(p.TestLists != null),
MapList(p.TestLists).ToArray(),
null
)
},
null)
说明 Map(p.TestClass) MapList(p.TestLists).ToArray(), 完整的信息为 Mapper<TestC,TestD>.Map() Mapper<TestC,TestD>.MapList()
总结:解决嵌套类的核心代码
//构造回调 Mapper<TSource, TTarget>.Map()
var mapperType = typeof(DataMapper<,>).MakeGenericType(sourceType, targetType);
var mapperExecMap = Expression.Call(mapperType.GetMethod(nameof(Map), new[] { sourceType }), sourceProperty);
利用Expression.Call 根据参数类型动态生成 对象映射的表达式
性能测试
写了这么多最终目的还是为了解决性能问题,下面将对比下性能
1、测试类
public static class MapperTest
{
//执行次数
public static int Count = 100000;
//简单类型
public static void Nomal()
{
Console.WriteLine($"******************简单类型:{Count / 10000}万次执行时间*****************");
var model = new TestA
{
Id =1,
Name = "张三",
};
//计时
var sw = Stopwatch.StartNew();
for (int i = 0; i < Count; i++)
{
if (model != null)
{
var b = new TestB
{
Id = model.Id,
Name = model.Name,
};
}
}
sw.Stop();
Console.WriteLine($"原生的时间:{sw.ElapsedMilliseconds}ms");
Exec(model);
}
//复杂类型
public static void Complex()
{
Console.WriteLine($"********************复杂类型:{Count / 10000}万次执行时间*********************");
var model = new TestA
{
Id = 1,
Name = "张三",
TestClass = new TestC
{
Id = 2,
Name = "lisi",
},
};
//计时
var sw = Stopwatch.StartNew();
for (int i = 0; i < Count; i++)
{
if (model != null)
{
var b = new TestB
{
Id = model.Id,
Name = model.Name,
};
if (model.TestClass != null)
{
b.TestClass = new TestD
{
Id = i,
Name = "lisi",
};
}
}
}
sw.Stop();
Console.WriteLine($"原生的时间:{sw.ElapsedMilliseconds}ms");
Exec(model);
}
//嵌套类型
public static void Nest()
{
Console.WriteLine($"*****************嵌套类型:{Count / 10000}万次执行时间*************************");
var model = new TestA
{
Id = 1,
Name = "张三",
TestClass = new TestC
{
Id = 1,
Name = "lisi",
SelfClass = new TestC
{
Id = 2,
Name = "lisi",
SelfClass = new TestC
{
Id = 3,
Name = "lisi",
SelfClass = new TestC
{
Id = 4,
Name = "lisi",
},
},
},
},
};
//计时
var item = model;
var sw = Stopwatch.StartNew();
for (int i = 0; i < Count; i++)
{
//这里每一步需要做非空判断的,书写太麻烦省去了
if (model != null)
{
var b = new TestB
{
Id = model.Id,
Name = model.Name,
TestClass = new TestD
{
Id = model.TestClass.Id,
Name = model.TestClass.Name,
SelfClass = new TestD
{
Id = model.TestClass.SelfClass.Id,
Name = model.TestClass.SelfClass.Name,
SelfClass = new TestD
{
Id = model.TestClass.SelfClass.SelfClass.Id,
Name = model.TestClass.SelfClass.SelfClass.Name,
SelfClass = new TestD
{
Id = model.TestClass.SelfClass.SelfClass.SelfClass.Id,
Name = model.TestClass.SelfClass.SelfClass.SelfClass.Name,
},
},
},
},
};
}
}
sw.Stop();
Console.WriteLine($"原生的时间:{sw.ElapsedMilliseconds}ms");
Exec(model);
}
//集合
public static void List()
{
Console.WriteLine($"********************集合类型:{Count/10000}万次执行时间***************************");
var model = new TestA
{
Id = 1,
Name = "张三",
TestLists = new List<TestC> {
new TestC{
Id = 1,
Name = "张三",
},
new TestC{
Id = -1,
Name = "张三",
},
}
};
//计时
var sw = Stopwatch.StartNew();
for (int i = 0; i < Count; i++)
{
var item = model;
if (item != null)
{
var b = new TestB
{
Id = item.Id,
Name = item.Name,
TestLists = new List<TestD> {
new TestD{
Id = item.Id,
Name = item.Name,
},
new TestD{
Id = -item.Id,
Name = item.Name,
},
}.ToArray()
};
}
}
sw.Stop();
Console.WriteLine($"原生的时间:{sw.ElapsedMilliseconds}ms");
Exec(model);
}
public static void Exec(TestA model)
{
//表达式
Mapper<TestA, TestB>.Map(model);
var sw = Stopwatch.StartNew();
for (int i = 0; i < Count; i++)
{
var b = Mapper<TestA, TestB>.Map(model);
}
sw.Stop();
Console.WriteLine($"表达式的时间:{sw.ElapsedMilliseconds}ms");
//AutoMapper
sw.Restart();
for (int i = 0; i < Count; i++)
{
var b = AutoMapper.Mapper.Map<TestA, TestB>(model);
}
sw.Stop();
Console.WriteLine($"AutoMapper时间:{sw.ElapsedMilliseconds}ms");
//TinyMapper
sw.Restart();
for (int i = 0; i < Count; i++)
{
var b = TinyMapper.Map<TestA, TestB>(model);
}
sw.Stop();
Console.WriteLine($"TinyMapper时间:{sw.ElapsedMilliseconds}ms");
}
}
2、调用测试
static void Main(string[] args)
{
AutoMapper.Mapper.Initialize(cfg => cfg.CreateMap<TestA, TestB>());
TinyMapper.Bind<TestA, TestB>();
Mapper<TestA, TestB>.Map(new TestA());
MapperTest.Count = 10000;
MapperTest.Nomal();
MapperTest.Complex();
MapperTest.Nest();
MapperTest.List();
MapperTest.Count = 100000;
MapperTest.Nomal();
MapperTest.Complex();
MapperTest.Nest();
MapperTest.List();
MapperTest.Count = 1000000;
MapperTest.Nomal();
MapperTest.Complex();
MapperTest.Nest();
MapperTest.List();
MapperTest.Count = 10000000;
MapperTest.Nomal();
MapperTest.Complex();
MapperTest.Nest();
MapperTest.List();
Console.WriteLine($"------------结束--------------------");
Console.ReadLine();
}
3、结果
1万次
10万次
100万次
1000万次
上图结果AutoMapper 在非简单类型的转换上比其他方案有50倍以上的差距,几乎就跟反射的结果一样。
看完本文有收获?请转发分享给更多人
关注「DotNet」,提升.Net技能
以上是关于c#表达式树最完善的表达式树Expression.Dynamic的玩法的主要内容,如果未能解决你的问题,请参考以下文章