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

  • 修改了 Validate 方法的实现 , 不再直接调用 ValidateCore, 而调用 _func 来进行验证 。
  • 运行该测试 , 开发者可以发现 , 其消耗的时间和上一步直接调用的耗时 , 几乎一样 , 没有额外消耗 。
  • 这里提供了一种最为简单的使用表达式进行动态调用的思路 , 如果可以写出一个静态方法(例如:ValidateCore)来表示动态调用的过程 。 那么我们只要使用类似于 Init 中的构建过程来构建表达式和委托即可 。
  • 开发者可以试着为 ValidateCore 增加第三个参数 name 以便拼接在错误信息中 , 从而了解如果构建这种简单的表达式 。
  • 第二步 , 组合表达式虽然前一步 , 我们将直接调用转变了动态调用 , 但由于 ValidateCore 还是写死的 , 因此还需要进一步修改 。
    本步骤 , 我们将会把 ValidateCore 中写死的三个 return 路径拆分为不同的方法 , 然后再采用表达式拼接在一起 。
    如果我们实现了 , 那么我们就有条件将更多的方法拼接在一起 , 实现一定程度的扩展 。
    注意:演示代码将瞬间边长 , 不必感受太大压力 , 可以辅助后面的代码要点说明进行查看 。
    using System;using System.ComponentModel.DataAnnotations;using System.Diagnostics;using System.Linq.Expressions;using FluentAssertions;using NUnit.Framework;// ReSharper disable InvalidXmlDocCommentnamespace Newbe.ExpressionsTests{////// Block Expression///public class X03PropertyValidationTest02{private const int Count = 10_000;private static Func _func;[SetUp]public void Init(){try{var finalExpression = CreateCore();_func = finalExpression.Compile();Expression> CreateCore(){// exp for inputvar inputExp = Expression.Parameter(typeof(CreateClaptrapInput), "input");var minLengthPExp = Expression.Parameter(typeof(int), "minLength");// exp for outputvar resultExp = Expression.Variable(typeof(ValidateResult), "result");// exp for return statementvar returnLabel = Expression.Label(typeof(ValidateResult));// build whole blockvar body = Expression.Block(new[] {resultExp},CreateDefaultResult(),CreateValidateNameRequiredExpression(),CreateValidateNameMinLengthExpression(),Expression.Label(returnLabel, resultExp));// build lambda from bodyvar final = Expression.Lambda>(body,inputExp,minLengthPExp);return final;Expression CreateDefaultResult(){var okMethod = typeof(ValidateResult).GetMethod(nameof(ValidateResult.Ok));Debug.Assert(okMethod != null, nameof(okMethod) + " != null");var methodCallExpression = Expression.Call(okMethod);var re = Expression.Assign(resultExp, methodCallExpression);/*** final as:* result = ValidateResult.Ok()*/return re;}Expression CreateValidateNameRequiredExpression(){var requireMethod = typeof(X03PropertyValidationTest02).GetMethod(nameof(ValidateNameRequired));var isOkProperty = typeof(ValidateResult).GetProperty(nameof(ValidateResult.IsOk));Debug.Assert(requireMethod != null, nameof(requireMethod) + " != null");Debug.Assert(isOkProperty != null, nameof(isOkProperty) + " != null");var requiredMethodExp = Expression.Call(requireMethod, inputExp);var assignExp = Expression.Assign(resultExp, requiredMethodExp);var resultIsOkPropertyExp = Expression.Property(resultExp, isOkProperty);var conditionExp = Expression.IsFalse(resultIsOkPropertyExp);var ifThenExp =Expression.IfThen(conditionExp,Expression.Return(returnLabel, resultExp));var re = Expression.Block(new[] {resultExp},assignExp,ifThenExp);/*** final as:* result = ValidateNameRequired(input);* if (!result.IsOk)* {*return result;* }*/return re;}Expression CreateValidateNameMinLengthExpression(){var minLengthMethod =typeof(X03PropertyValidationTest02).GetMethod(nameof(ValidateNameMinLength));var isOkProperty = typeof(ValidateResult).GetProperty(nameof(ValidateResult.IsOk));Debug.Assert(minLengthMethod != null, nameof(minLengthMethod) + " != null");Debug.Assert(isOkProperty != null, nameof(isOkProperty) + " != null");var requiredMethodExp = Expression.Call(minLengthMethod, inputExp, minLengthPExp);var assignExp = Expression.Assign(resultExp, requiredMethodExp);var resultIsOkPropertyExp = Expression.Property(resultExp, isOkProperty);var conditionExp = Expression.IsFalse(resultIsOkPropertyExp);var ifThenExp =Expression.IfThen(conditionExp,Expression.Return(returnLabel, resultExp));var re = Expression.Block(new[] {resultExp},assignExp,ifThenExp);/*** final as:* result = ValidateNameMinLength(input, minLength);* if (!result.IsOk)* {*return result;* }*/return re;}}}catch (Exception e){Console.WriteLine(e);throw;}}[Test]public void Run(){// see code in demo repo}public static ValidateResult Validate(CreateClaptrapInput input){return _func.Invoke(input, 3);}public static ValidateResult ValidateNameRequired(CreateClaptrapInput input){return string.IsNullOrEmpty(input.Name)? ValidateResult.Error("missing Name"): ValidateResult.Ok();}public static ValidateResult ValidateNameMinLength(CreateClaptrapInput input, int minLength){return input.Name.Length