靜態編譯要求開發期已知型別與方法簽章。動態呼叫適用於:外掛架構、腳本引擎、延遲載入組件、根據設定檔決定實作類別、測試框架自動發現測試方法等場景。核心 API:System.Reflection.Assembly.Load、Type.GetType、Activator.CreateInstance、MethodInfo.Invoke / Type.InvokeMember。
Class1(靜態/實例方法、帶參數方法)// Class1.cs → 編譯:csc /t:library Class1.cs
using System;
public class Class1
{
public static string StaticMethod() => "I am StaticMethod in Class1";
public string InstanceMethod() => "I am InstanceMethod in Class1";
public string MethodWithParam(string name) => "Hello " + name;
}
// DynaInvoke.cs → 編譯:csc /r:ClassLibrary.dll DynaInvoke.cs
using System;
using System.Reflection;
public class DynaInvoke
{
public static void Main()
{
// 1. 載入組件(可用檔案路徑、強名稱、位元組陣列)
Assembly asm = Assembly.LoadFrom("ClassLibrary.dll"); // 或 Assembly.Load("ClassLibrary")
// 2. 取得型別
Type t = asm.GetType("Class1"); // 命名空間.類別名
if (t == null) { Console.WriteLine("型別不存在"); return; }
// 3. 呼叫靜態方法
object staticResult = t.InvokeMember(
"StaticMethod",
BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod,
null, null, new object[] { });
Console.WriteLine($"靜態方法結果:{staticResult}");
// 4. 建立實例並呼叫實例方法
object instance = Activator.CreateInstance(t);
object instanceResult = t.InvokeMember(
"InstanceMethod",
BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod,
null, instance, new object[] { });
Console.WriteLine($"實例方法結果:{instanceResult}");
// 5. 呼叫帶參數方法
object paramResult = t.InvokeMember(
"MethodWithParam",
BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod,
null, instance, new object[] { "Kunal" });
Console.WriteLine($"帶參數方法結果:{paramResult}");
}
}
| 方法 | 載入上下文 | 適用場景 |
|---|---|---|
Load(assemblyName) | Load | GAC 或應用程式基底目錄的強名稱組件 |
LoadFrom(path) | LoadFrom | 任意路徑,支援相依組件自動解析 |
LoadFile(path) | Neither | 完全控制,不解析相依性,需手動處理 |
Load(byte[]) | Load | 從資料庫、網路下載的位元組陣列載入 |
Public | Instance | InvokeMethod:公開實例方法(最常用)Public | Static | InvokeMethod:公開靜態方法NonPublic | Instance | InvokeMethod:私有/受保護實例方法GetProperty | SetProperty:屬性存取GetField | SetField:欄位存取CreateInstance:建構函式(搭配 Activator.CreateInstance 更直觀)// InvokeMember:一行完成繫結+調用,適合簡單場景
object result = t.InvokeMember("Method", BindingFlags.InvokeMethod, null, instance, args);
// MethodInfo:分離繫結與調用,效能較好(可快取 MethodInfo)、支援開放泛型委派
MethodInfo mi = t.GetMethod("Method");
object result = mi.Invoke(instance, args);
// 開放泛型委派(.NET 2.0+)最高效能
Func
MethodInfo openGeneric = t.GetMethod("GenericMethod");
MethodInfo closedGeneric = openGeneric.MakeGenericMethod(typeof(int), typeof(string));
closedGeneric.Invoke(instance, new object[] { 42, "test" });
// .NET 4.5+ Task-based
Task task = Task.Run(() => mi.Invoke(instance, args));
object result = await task;
dynamicdynamic obj = Activator.CreateInstance(t);
string result = obj.InstanceMethod(); // DLR 快取呼叫點,次數多時效能優於反射
string result2 = obj.MethodWithParam("Dynamic");
注意:dynamic 無法呼叫靜態方法、非公用成員,且編譯期不檢查,執行期失敗拋 RuntimeBinderException。
Assembly.LoadFrom 可能繞過強名稱驗證AppDomain 隔離(.NET Framework)或 AssemblyLoadContext(.NET Core)ReflectionPermission、SecurityPermission(非公用成員存取)MethodInfo/PropertyInfo/FieldInfo 物件dynamicGetType/GetMethodExpression Trees 編譯 Lambda 委派(.NET 3.5+)動態方法呼叫是 C# 反射機制的核心應用。從 Assembly.Load 到 InvokeMember/MethodInfo.Invoke,再到現代 dynamic/Expression/Source Generators,工具鏈不斷進化。掌握 BindingFlags、載入上下文、快取策略、安全邊界,才能在外掛系統、ORM、序列化器、測試框架等基礎設施中游刃有餘。