首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 動態方法呼叫:Reflection 與 Assembly.Load 實現執行期型別發現與調用

     編輯:關於C語言

前言:為何需要動態呼叫?

靜態編譯要求開發期已知型別與方法簽章。動態呼叫適用於:外掛架構、腳本引擎、延遲載入組件、根據設定檔決定實作類別、測試框架自動發現測試方法等場景。核心 API:System.Reflection.Assembly.Load、Type.GetType、Activator.CreateInstance、MethodInfo.Invoke / Type.InvokeMember。

範例架構

  • ClassLibrary.dll:包含目標類別 Class1(靜態/實例方法、帶參數方法)
  • DynaInvoke.exe:執行期載入 DLL、反射呼叫方法

目標組件原始碼

// 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}");
    }
}

關鍵 API 深度解析

Assembly.Load vs LoadFrom vs LoadFile

方法載入上下文適用場景
Load(assemblyName)LoadGAC 或應用程式基底目錄的強名稱組件
LoadFrom(path)LoadFrom任意路徑,支援相依組件自動解析
LoadFile(path)Neither完全控制,不解析相依性,需手動處理
Load(byte[])Load從資料庫、網路下載的位元組陣列載入

BindingFlags 組合藝術

  • Public | Instance | InvokeMethod:公開實例方法(最常用)
  • Public | Static | InvokeMethod:公開靜態方法
  • NonPublic | Instance | InvokeMethod:私有/受保護實例方法
  • GetProperty | SetProperty:屬性存取
  • GetField | SetField:欄位存取
  • CreateInstance:建構函式(搭配 Activator.CreateInstance 更直觀)

InvokeMember vs MethodInfo.Invoke

// 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 del = (Func)Delegate.CreateDelegate(
    typeof(Func), mi);
string result = del(instance);

進階主題

1. 泛型方法動態呼叫

MethodInfo openGeneric = t.GetMethod("GenericMethod");
MethodInfo closedGeneric = openGeneric.MakeGenericMethod(typeof(int), typeof(string));
closedGeneric.Invoke(instance, new object[] { 42, "test" });

2. 非同步動態呼叫

// .NET 4.5+ Task-based
Task task = Task.Run(() => mi.Invoke(instance, args));
object result = await task;

3. 動態關鍵字:C# 4.0+ dynamic

dynamic obj = Activator.CreateInstance(t);
string result = obj.InstanceMethod(); // DLR 快取呼叫點,次數多時效能優於反射
string result2 = obj.MethodWithParam("Dynamic");

注意:dynamic 無法呼叫靜態方法、非公用成員,且編譯期不檢查,執行期失敗拋 RuntimeBinderException。

4. 安全性考量

  • 載入非受信組件風險:使用 Assembly.LoadFrom 可能繞過強名稱驗證
  • 沙箱執行:AppDomain 隔離(.NET Framework)或 AssemblyLoadContext(.NET Core)
  • 權限需求:ReflectionPermission、SecurityPermission(非公用成員存取)

效能優化清單

  1. 快取 MethodInfo/PropertyInfo/FieldInfo 物件
  2. 熱路徑使用開放泛型委派或 dynamic
  3. 避免在迴圈中重複 GetType/GetMethod
  4. 考慮 Expression Trees 編譯 Lambda 委派(.NET 3.5+)

總結

動態方法呼叫是 C# 反射機制的核心應用。從 Assembly.Load 到 InvokeMember/MethodInfo.Invoke,再到現代 dynamic/Expression/Source Generators,工具鏈不斷進化。掌握 BindingFlags、載入上下文、快取策略、安全邊界,才能在外掛系統、ORM、序列化器、測試框架等基礎設施中游刃有餘。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved