前言
稍微有經驗的.net工程師一定聽過或使用過Reflection,Reflection雖然好用(能動態處理很多事情)但對於效能會有些影響.
我能否擁有Reflection的動態彈性且兼顧效能呢?
有:就是我們這次要介紹的Expression.
Expression vs Reflection performace
我會準備一個範例來比較Expression
和Reflection
效能差異
Expression
Activator.CreateInstance
Activator.CreateInstance Code
Activator.CreateInstance
沒甚麼好說就是一個靜態方法傳入Type
動態產生一個物件
Expression code
Expression程式碼如下,能發現只是為了建立一個物件需要寫一大堆程式碼(但這些程式碼對於追求效能的你是必須的)
1 | delegate T Func<T>(params object[] args); |
產生出來Lambda程式碼如下,之後我們就可以透過此lambda來產生我們要物件摟
1 | .Lambda |
BenchmarkDotNet分析
Sample Project放在GitHub ExpressionVsReflection
1 | public delegate T Func<T>(params object[] args); |
1 |
|
上面顯示使用Expression效能比起使用Reflection有明顯提升.
Expression小提醒
如果使用Expression或Emit技術時,產生的程式碼(委派)記得使用Cache存放起來,因為如果每次執行都運算Compile效能反而會比Reflection還要更差.
如果沒有使用Cache來執行Expression效率就大大降低甚至比Reflection還要差.
1 | | Method | Mean | StdDev | Error | Gen 0 | Gen 1 | Gen 2 | Allocated | |
常用Expression解說
透過上面範例,能發現Expression核心概念是用來產生Delegate程式碼並呼叫使用.
因為動作最小單位是方法,委派可以視做方法
- Expression.Call 呼叫委派方法
- Note:如果呼叫static方法第一個參數是null
- Expression.Assign 對於Expression給值 ex: expression1 = expression2
- Expression.Block 大括號區域 ex:
{}
- Expression.Convert 轉型
- Expression.Multiply 乘法
- Expression.Bind 綁定物件屬性,成員
- Expression.MemberInit 建構子成員初始化
- (BinaryExpression)
- Expression.GreaterThanOrEqual:大於等於
- Expression.GreaterThan:大於
- Expression.LessThanOrEqual:小於等於
- Expression.LessThan:小於
- Expression.Lambda 封裝成方法
- Expression.New 建立New語法
ArrayAccess vs ArrayIndex
- ArrayIndex 是只讀Index
- ArrayAccess 可讀可寫
https://stackoverflow.com/questions/14973813/arrayaccess-vs-arrayindex-in-expression-tree
小結
Expression和Emit雖然難寫,但寫的好可以讓程式碼更有彈性且比Reflection更有效能,Expression在許多知名架構都有使用(包含微軟MVC框架也是使用Reflection + Expression來優化),舉一個例子動態代理就很適合使用Expression
或Emit
來優化.
知名動態代理框架castle有使用到Emit.
Emit想要解決的問題和Expression類似,只是Emit提供更多底層API讓我們呼叫(比Expression可以控制更多細節)
Emit可以寫類似IL程式語法
想必然Emit寫起來也更繁瑣更容易出錯.
所以了解Expression是前往.net進階工程師必經之路.
此文作者:Daniel Shih(石頭)
此文地址: https://isdaniel.github.io/expression-vs-reflection/
版權聲明:本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 3.0 TW 許可協議。轉載請註明出處!