[C#.NET] 利用 dynamic 簡化反射程式碼?

[C#.NET] 利用 dynamic 簡化反射程式碼?

dynamic 是 .NET Framework 4.0 產出的語法,這讓 C# 擁有了弱語言的特性,編譯器在編譯的時候不對 dynamic 型別進行檢查,有人會將 var 與 dynamic 做比較,在 C# 裡這兩者是完成不同的產物,var 是語法糖,它會在編譯時期幫我們轉成正確的型別,在VS底下仍能使用 Intellisense;

image

 

而 dynamic 在編譯時期長的像一個 object,它在編譯時期不做檢查,所以沒有 Intellisense 可以使用,VS 也不會告知你有錯誤;它到執行時期時才會檢查,所以可自行輸入成員。

image

接下來我們來看一點例子:

我有一個 Dynamic 元件,裡面有 Util 類別

{
    public class Util
    {
        public string FirstName { get; set; }

        public string LastName { get; set; }

        private string FullName
        {
            get { return string.Format("Full Name: {0} , {1}", this.LastName, this.FirstName); }
        }

        public Util()
        {
        }

        private Util(string FirstName, string LastName)
        {
            this.FirstName = FirstName;
            this.LastName = LastName;
        }

        protected string _print(string FirstName, string LastName)
        {
            var result = string.Format("Full Name: {0} , {1}", LastName, FirstName);
            return result;
        }

        public int Sum(int a, int b)
        {
            var result = a + b;
            return result;
        }
    }
}


我在 winform 專案裡想要反射 Dynamic.dll

一般反射公開方法的寫法

private static string CLASS_NAME = "Dynamic.Util";
private static string METHOD_PRINT = "_print";
private static string METHOD_SUM = "Sum";
private int? GetSum(int a, int b)
{
    //載入組件
    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;

    //建立參數
    object[] para = new object[] { a, b };

    var methodInfo = assemblyType.GetMethod(METHOD_SUM, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

    //調用方法
    var result = (int)methodInfo.Invoke(instance, para);
    return result;
}

使用 dynamic 建立反射,這看起來就簡潔多了

{
    //載入組件
    var assembly = Assembly.Load(ASSEMBLY_NAME);

    //建立執行個體
    dynamic instance = assembly.CreateInstance(CLASS_NAME);

    //調用方法
    var result = instance.Sum(a, b);
    return result;
}

很明顯的,這兩種方法在調用 Sum 方法時效率會差很多


一般反射保護方法的寫法

{
    //載入組件
    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;

    //建立參數
    object[] para = new object[] { firstName, lastName };

    //取得類別中的protected方法
    var methodInfo = assemblyType.GetMethod(METHOD_PRINT, BindingFlags.Instance | BindingFlags.NonPublic);

    //調用方法
    var result = methodInfo.Invoke(instance, para).ToString();
    return result;
}

 

利用 dynamic 反射類別的保護方法,本以為可以直接調用_print方法,執行的時後,VS 會很不客氣的拋出例外

image

 

遇到了非 public 成員,dynamic 在寫法上討不到什麼便宜

{
    //載入組件
    var assembly = Assembly.Load(ASSEMBLY_NAME);

    //取得組件類別
    var assemblyType = assembly.GetType(CLASS_NAME);
    if (assemblyType == null) return null;

    //建立執行個體
    dynamic instance = Activator.CreateInstance(assemblyType);

    //建立參數
    var para = new object[] { firstName, lastName };

    //調用方法
    dynamic result = assemblyType.InvokeMember(
              METHOD_PRINT,
              BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public,
              null,
              instance,
              para);

    return result;
}

private string GetPrivateObject(string firstName, string lastName)
{
    //載入組件
    var assembly = Assembly.Load(ASSEMBLY_NAME);

    //取得組件類別
    var assemblyType = assembly.GetType(CLASS_NAME);

    //建立執行個體
    var instance = Activator.CreateInstance(assemblyType);

    dynamic util = instance.AsDynamic();

    //調用 protected method
    var result = util._print(firstName, lastName);
    return result;
}

private string GetPrivateObject1(string firstName, string lastName)
{
    //載入組件
    var assembly = Assembly.Load(ASSEMBLY_NAME);

    //取得組件類別
    var assemblyType = assembly.GetType(CLASS_NAME);
    var para = new object[] { firstName, lastName };

    //建立private執行個體
    var instance = Activator.CreateInstance(assemblyType, (BindingFlags.Instance | BindingFlags.NonPublic), null, para, null);
    dynamic util = instance.AsDynamic();

    //調用 private property
    dynamic result = util.FullName;
    return result;
}


另外,這裡還有一些能讓提昇反射效率的文章及元件

Fasterflect vs. C# 4.0 Dynamic

Fasterflect - a fast and simple API for Reflection invocation

Fasterflect - .NET Reflection Made Fast and Simple

 

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


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

Image result for microsoft+mvp+logo