只要十步,你就可以应用表达式树来优化动态调用(一)
表达式树是 .net 中一系列非常好用的类型 。 在一些场景中使用表达式树可以获得更好的性能和更佳的扩展性 。 本篇我们将通过构建一个 “模型验证器” 来理解和应用表达式树在构建动态调用方面的优势 。
Newbe.Claptrap 是一个用于轻松应对并发问题的分布式开发框架 。 如果您是首次阅读本系列文章 。 建议可以先从本文末尾的入门文章开始了解 。
开篇摘要前不久 , 我们发布了《如何使用 dotTrace 来诊断 netcore 应用的性能问题》 , 经过网友投票之后 , 网友们表示对其中表达式树的内容很感兴趣 , 因此本篇我们将展开讲讲 。
动态调用是在 .net 开发是时常遇到的一种需求 , 即在只知道方法名或者属性名等情况下动态的调用方法或者属性 。 最广为人知的一种实现方式就是使用 “反射” 来实现这样的需求 。 当然也有一些高性能场景会使用 Emit 来完成这个需求 。
本文将介绍 “使用表达式树” 来实现这种场景 , 因为这个方法相较于 “反射” 将拥有更好的性能和扩展性 , 相较于 Emit 又更容易掌握 。
我们将使用一个具体的场景来逐步使用表达式来实现动态调用 。
在该场景中 , 我们将构建一个模型验证器 , 这非常类似于 aspnet mvc 中 ModelState 的需求场景 。
这不是一篇简单的入门文章 , 初次涉足该内容的读者 , 建议在空闲时 , 在手边有 IDE 可以顺便操作时边看边做 。 同时 , 也不必在意样例中出现的细节方法 , 只需要了解其中的大意 , 能够依样画瓢即可 , 掌握大意之后再深入了解也不迟 。
为了缩短篇幅 , 文章中的样例代码会将没有修改的部分隐去 , 想要获取完整的测试代码 , 请打开文章末尾的代码仓库进行拉取 。
为什么要用表达式树 , 为什么可以用表达式树?首先需要确认的事情有两个:
- 使用表达式树取代反射是否有更好的性能?
- 使用表达式树进行动态调用是否有很大的性能损失?
调用一个对象的方法:
using System;using System.Diagnostics;using System.Linq.Expressions;using System.Reflection;using FluentAssertions;using NUnit.Framework;namespace Newbe.ExpressionsTests{public class X01CallMethodTest{private const int Count = 1_000_000;private const int Diff = 100;[SetUp]public void Init(){_methodInfo = typeof(Claptrap).GetMethod(nameof(Claptrap.LevelUp));Debug.Assert(_methodInfo != null, nameof(_methodInfo) + " != null");var instance = Expression.Parameter(typeof(Claptrap), "c");var levelP = Expression.Parameter(typeof(int), "l");var callExpression = Expression.Call(instance, _methodInfo, levelP);var lambdaExpression = Expression.Lambda>(callExpression, instance, levelP);// lambdaExpression should be as (Claptrap c,int l) =>{ c.LevelUp(l); }_func = lambdaExpression.Compile();}[Test]public void RunReflection(){var claptrap = new Claptrap();for (int i = 0; i < Count; i++){_methodInfo.Invoke(claptrap, new[] {(object) 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.LevelUp(Diff);}claptrap.Level.Should().Be(Count * Diff);}private MethodInfo _methodInfo;private Action _func;public class Claptrap{public int Level { get; set; }public void LevelUp(int diff){Level += diff;}}}}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 小米11迎强敌,配120W超充,0到100%只要15分钟
- 只需799元就可以把旧iPad换成新款iPad?是真的!但这羊毛可没那么容易薅
- 马化腾这招太高明!网友:只要我改名的速度够快,禁令就追不上我
- 飞腾桌面腾锐D2000 CPU发布:最高2.6GHz、八核只要25W
- 苹果官网产品出现 Bug 价:上千元产品显示只要一两百元
- 华为手机为什么耗电快?只要关闭手机这6个开关,2天1充不是梦
- 苏宁易购双十二10亿补贴引爆吃货狂欢,西贝明星单品只要9.9
- 别错过,双节钜惠还有2天结束
- 人群|出差旅行也能带呼吸机?M1 Mini就可以
- 瑞士宽带商TWiFi可提供18年免费上网服务