[C#.NET] 利用 dynamic 簡化反射程式碼?
dynamic 是 .NET Framework 4.0 產出的語法,這讓 C# 擁有了弱語言的特性,編譯器在編譯的時候不對 dynamic 型別進行檢查,有人會將 var 與 dynamic 做比較,在 C# 裡這兩者是完成不同的產物,var 是語法糖,它會在編譯時期幫我們轉成正確的型別,在VS底下仍能使用 Intellisense;
而 dynamic 在編譯時期長的像一個 object,它在編譯時期不做檢查,所以沒有 Intellisense 可以使用,VS 也不會告知你有錯誤;它到執行時期時才會檢查,所以可自行輸入成員。
接下來我們來看一點例子:
我有一個 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 會很不客氣的拋出例外
遇到了非 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