[C#.NET] 利用 Expression Tree 提昇反射效率
今天 Bibby 在 MSN 敲我,除了閒話家常約吃飯之外,還對我上篇 [C#.NET] 利用 dynamic 簡化反射程式碼?,提出了些想法:「不知 dynamic 與 Expression Tree 誰比較屌」
Expression Tree 是.NET 3.5 的產物,我們只知道可以利用它來進行反射之外,其他實際上的應用就不太瞭解,從對岸的文章找到相關多的資料,把它記錄下來,有機會再好好研究它一番
先來看看 Expression Tree 的寫法,仍是引用上一篇的Util 類別
{
//載入組件
var assembly = Assembly.Load(ASSEMBLY_NAME);
//取得組件類別
var assemblyType = assembly.GetType(CLASS_NAME);
if (assemblyType == null) return null;
//建立執行個體
var instance = Activator.CreateInstance(assemblyType);
if (instance == null)
return null;
//取得方法
var methodInfo = assemblyType.GetMethod(METHOD_SUM);
//建立參數
ParameterExpression param1 = Expression.Parameter(typeof(int), "a");
ParameterExpression param2 = Expression.Parameter(typeof(int), "b");
MethodCallExpression call = Expression.Call(Expression.Constant(instance), methodInfo, new ParameterExpression[] { param1, param2 });
var action = Expression.Lambda<Func<int, int, int>>(call, param1, param2).Compile();
//調用方法
var result = action(a, b);
return result;
}
它跟傳統的反射一樣都要先定義參數,然後才能調用方法,這段程式碼看起來不如 dynamic 寫法的精簡,不知道效能如何?
我為不同的反射方式分別建立以下程式碼,然後觀察調用公開方法花費的時間,當然你也可以自己動手寫非公開方法。
{
//載入組件
var assembly = Assembly.Load(ASSEMBLY_NAME);
//取得組件類別
var assemblyType = assembly.GetType(CLASS_NAME);
if (assemblyType == null) return null;
//建立執行個體
var instance = Activator.CreateInstance(assemblyType);
if (instance == null) return null;
var methodInfo = assemblyType.GetMethod(METHOD_SUM);
Random rnd = new Random();
int a = 1;
int b = 1;
Stopwatch watch = new Stopwatch();
watch.Start();
for (int i = 0; i < this.m_Iteration; i++)
{
//a = rnd.Next(0, 100);
//b = rnd.Next(0, 100);
//建立參數
object[] para = new object[] { a, b };
//調用方法
var result = (int)methodInfo.Invoke(instance, para);
}
watch.Stop();
return watch.ElapsedMilliseconds;
}
private long? GetDynamicSum()
{
//載入組件
var assembly = Assembly.Load(ASSEMBLY_NAME);
//建立執行個體
dynamic instance = assembly.CreateInstance(CLASS_NAME);
Random rnd = new Random();
int a = 1;
int b = 1;
Stopwatch watch = new Stopwatch();
watch.Start();
for (int i = 0; i < this.m_Iteration; i++)
{
//a = rnd.Next(0, 100);
//b = rnd.Next(0, 100);
//調用方法
var result = instance.Sum(a, b);
}
watch.Stop();
return watch.ElapsedMilliseconds;
}
private long? GetExpressionSum()
{
//載入組件
var assembly = Assembly.Load(ASSEMBLY_NAME);
//取得組件類別
var assemblyType = assembly.GetType(CLASS_NAME);
if (assemblyType == null) return null;
//建立執行個體
var instance = Activator.CreateInstance(assemblyType);
if (instance == null)
return null;
//取得方法
var methodInfo = assemblyType.GetMethod(METHOD_SUM);
//建立參數
ParameterExpression param1 = Expression.Parameter(typeof(int), "a");
ParameterExpression param2 = Expression.Parameter(typeof(int), "b");
MethodCallExpression call = Expression.Call(Expression.Constant(instance), methodInfo, new ParameterExpression[] { param1, param2 });
var action = Expression.Lambda<Func<int, int, int>>(call, param1, param2).Compile();
//建立亂數
Random rnd = new Random();
int a = 1;
int b = 1;
Stopwatch watch = new Stopwatch();
watch.Start();
for (int i = 0; i < this.m_Iteration; i++)
{
//a = rnd.Next(0, 100);
//b = rnd.Next(0, 100);
//調用方法
var result = action(a, b);
}
watch.Stop();
return watch.ElapsedMilliseconds;
}
比較畫面如下,很明顯的 Expression Tree 在執行大量動作時可以得到較佳的效能
範例下載:
參考來源
C# Properties and Expression Trees
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET