[C#.NET] 利用 Expression Tree 提昇反射效率

  • 20457
  • 0
  • C#
  • 2021-05-13

[C#.NET] 利用 Expression Tree 提昇反射效率

今天 Bibby 在 MSN 敲我,除了閒話家常約吃飯之外,還對我上篇 [C#.NET] 利用 dynamic 簡化反射程式碼?,提出了些想法:「不知 dynamic 與 Expression Tree 誰比較屌」

Expression Tree 是.NET 3.5 的產物,我們只知道可以利用它來進行反射之外,其他實際上的應用就不太瞭解,從對岸的文章找到相關多的資料,把它記錄下來,有機會再好好研究它一番

Expression Tree 上手指南 (一)

Expression Tree 上手指南 (二)

Expression Tree 上手指南 (三)

 

 


先來看看 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 在執行大量動作時可以得到較佳的效能

SNAGHTMLb4c93b1

 

範例下載:

ReflectionComparison.zip


參考來源

Expression Tree vs reflection

C# Properties and Expression Trees

 

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo