只要十步,你就可以应用表达式树来优化动态调用(一)( 五 )
< minLength? ValidateResult.Error($"Length of Name should be great than {minLength}"): ValidateResult.Ok();}}}代码要点:
- ValidateCore 方法被拆分为了 ValidateNameRequired 和 ValidateNameMinLength 两个方法 , 分别验证 Name 的 Required 和 MinLength 。
- Init 方法中使用了 local function 从而实现了方法 “先使用后定义” 的效果 。 读者可以自上而下阅读 , 从顶层开始了解整个方法的逻辑 。
- Init 整体的逻辑就是通过表达式将 ValidateNameRequired 和 ValidateNameMinLength 重新组合成一个形如 ValidateCore 的委托 Func
。 - Expression.Parameter 用于标明委托表达式的参数部分 。
- Expression.Variable 用于标明一个变量 , 就是一个普通的变量 。 类似于代码中的 var a 。
- Expression.Label 用于标明一个特定的位置 。 在该样例中 , 主要用于标定 return 语句的位置 。 熟悉 goto 语法的开发者知道 ,goto 的时候需要使用 label 来标记想要 goto 的地方 。 而实际上 , return 就是一种特殊的 goto 。 所以想要在多个语句块中 return 也同样需要标记后才能 return 。
- Expression.Block 可以将多个表达式顺序组合在一起 。 可以理解为按顺序写代码 。 这里我们将 CreateDefaultResult、CreateValidateNameRequiredExpression、CreateValidateNameMinLengthExpression 和 Label 表达式组合在一起 。 效果就类似于把这些代码按顺序拼接在一起 。
- CreateValidateNameRequiredExpression 和 CreateValidateNameMinLengthExpression 的结构非常类似 , 因为想要生成的结果表达式非常类似 。
- 不必太在意 CreateValidateNameRequiredExpression 和 CreateValidateNameMinLengthExpression 当中的细节 。 可以在本样例全部阅读完之后再尝试了解更多的 Expression.XXX 方法 。
- 经过这样的修改之后 , 我们就实现了扩展 。 假设现在需要对 Name 增加一个 MaxLength 不得超过 16 的验证 。 只需要增加一个 ValidateNameMaxLength 的静态方法 , 添加一个 CreateValidateNameMaxLengthExpression 的方法 , 并且加入到 Block 中即可 。 读者可以尝试动手操作一波实现这个效果 。
我们将改造这两个方法 , 使其传入 string name 表示验证的属性名称 , string value 表示验证的属性值 。 这样我们就可以将这两个验证方法用于不限于 Name 的更多属性 。
using System;using System.ComponentModel.DataAnnotations;using System.Diagnostics;using System.Linq.Expressions;using FluentAssertions;using NUnit.Framework;// ReSharper disable InvalidXmlDocCommentnamespace Newbe.ExpressionsTests{////// Property Expression///public class X03PropertyValidationTest03{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 nameProp = typeof(CreateClaptrapInput).GetProperty(nameof(CreateClaptrapInput.Name));Debug.Assert(nameProp != null, nameof(nameProp) + " != null");var namePropExp = Expression.Property(inputExp, nameProp);var nameNameExp = Expression.Constant(nameProp.Name);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
- 只需799元就可以把旧iPad换成新款iPad?是真的!但这羊毛可没那么容易薅
- 马化腾这招太高明!网友:只要我改名的速度够快,禁令就追不上我
- 飞腾桌面腾锐D2000 CPU发布:最高2.6GHz、八核只要25W
- 苹果官网产品出现 Bug 价:上千元产品显示只要一两百元
- 华为手机为什么耗电快?只要关闭手机这6个开关,2天1充不是梦
- 苏宁易购双十二10亿补贴引爆吃货狂欢,西贝明星单品只要9.9
- 别错过,双节钜惠还有2天结束
- 人群|出差旅行也能带呼吸机?M1 Mini就可以
- 瑞士宽带商TWiFi可提供18年免费上网服务
- 5G新选择:支持65W快充,8G+128G只要1599元