只要十步,你就可以应用表达式树来优化动态调用(一)( 二 )

以上测试中 , 我们对第三种调用方式一百万次调用 , 并记录每个测试所花费的时间 。 可以得到类似以下的结果:
MethodTimeRunReflection 217ms
RunExpression 20ms
Directly 19ms
可以得出以下结论:

  1. 使用表达式树创建委托进行动态调用可以得到和直接调用近乎相同的性能 。
  2. 使用表达式树创建委托进行动态调用所消耗的时间约为十分之一 。
所以如果仅仅从性能上考虑 , 应该使用表达式树 , 也可以是用表达式树 。
不过这是在一百万调用下体现出现的时间 , 对于单次调用而言其实就是纳秒级别的区别 , 其实无足轻重 。
但其实表达式树不仅仅在性能上相较于反射更优 , 其更强大的扩展性其实采用最为重要的特性 。
此处还有一个对属性进行操作的测试 , 此处将测试代码和结果罗列如下:
using System;using System.Diagnostics;using System.Linq.Expressions;using System.Reflection;using FluentAssertions;using NUnit.Framework;namespace Newbe.ExpressionsTests{public class X02PropertyTest{private const int Count = 1_000_000;private const int Diff = 100;[SetUp]public void Init(){_propertyInfo = typeof(Claptrap).GetProperty(nameof(Claptrap.Level));Debug.Assert(_propertyInfo != null, nameof(_propertyInfo) + " != null");var instance = Expression.Parameter(typeof(Claptrap), "c");var levelProperty = Expression.Property(instance, _propertyInfo);var levelP = Expression.Parameter(typeof(int), "l");var addAssignExpression = Expression.AddAssign(levelProperty, levelP);var lambdaExpression = Expression.Lambda>(addAssignExpression, instance, levelP);// lambdaExpression should be as (Claptrap c,int l) =>{ c.Level += l; }_func = lambdaExpression.Compile();}[Test]public void RunReflection(){var claptrap = new Claptrap();for (int i = 0; i < Count; i++){var value = http://kandian.youth.cn/index/(int) _propertyInfo.GetValue(claptrap);_propertyInfo.SetValue(claptrap, value + Diff);}claptrap.Level.Should().Be(Count * Diff);}[Test]public void RunExpression(){var claptrap = new Claptrap();for (int i = 0; i < Count; i++){_func.Invoke(claptrap, Diff);}claptrap.Level.Should().Be(Count * Diff);}[Test]public void Directly(){var claptrap = new Claptrap();for (int i = 0; i < Count; i++){claptrap.Level += Diff;}claptrap.Level.Should().Be(Count * Diff);}private PropertyInfo _propertyInfo;private Action _func;public class Claptrap{public int Level { get; set; }}}}耗时情况:
MethodTimeRunReflection 373ms
RunExpression 19ms
Directly 18ms
由于反射多了一份装拆箱的消耗 , 所以比起前一个测试样例显得更慢了 , 使用委托是没有这种消耗的 。
第〇步 , 需求演示先通过一个测试来了解我们要创建的 “模型验证器” 究竟是一个什么样的需求 。
using System.ComponentModel.DataAnnotations;using FluentAssertions;using NUnit.Framework;namespace Newbe.ExpressionsTests{////// Validate data by static method///public class X03PropertyValidationTest00{private const int Count = 10_000;[Test]public void Run(){for (int i = 0; i < Count; i++){// test 1{var input = new CreateClaptrapInput();var (isOk, errorMessage) = Validate(input);isOk.Should().BeFalse();errorMessage.Should().Be("missing Name");}// test 2{var input = new CreateClaptrapInput{Name = "1"};var (isOk, errorMessage) = Validate(input);isOk.Should().BeFalse();errorMessage.Should().Be("Length of Name should be great than 3");}// test 3{var input = new CreateClaptrapInput{Name = "yueluo is the only one dalao"};var (isOk, errorMessage) = Validate(input);isOk.Should().BeTrue();errorMessage.Should().BeNullOrEmpty();}}}public static ValidateResult Validate(CreateClaptrapInput input){return ValidateCore(input, 3);}public static ValidateResult ValidateCore(CreateClaptrapInput input, int minLength){if (string.IsNullOrEmpty(input.Name)){return ValidateResult.Error("missing Name");}if (input.Name.Length