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

C# 反射程式設計實戰:動態載入、型別檢視與方法調用完整範例

     編輯:關於C語言

反射核心應用場景

  • 外掛架構動態載入組件與型別
  • 序列化/反序列化引擎
  • ORM 框架實體映射
  • 單元測試框架自動發現測試方法
  • 依賴注入容器型別掃描與實例化

完整示範程式


using System;
using System.Reflection;
using System.Reflection.Emit;

public class TestReflection {
    private string file = @"TestReflection.exe";

    static void Main(string[] args) {
        TestReflection test = new TestReflection();
        test.DisplayModules();
        test.DisplayTypes();
        test.DisplayMethods();
        test.InvokeStaticMethod();
        test.InvokeMethod();
    }

    // 顯示所有模組
    public void DisplayModules() {
        Console.WriteLine("display modules ...");
        Assembly assembly = Assembly.LoadFrom(file);
        foreach (Module module in assembly.GetModules())
            Console.WriteLine("module name: " + module.Name);
    }

    // 顯示所有型別
    public void DisplayTypes() {
        Console.WriteLine("display types ...");
        Assembly assembly = Assembly.LoadFrom(file);
        foreach (Type type in assembly.GetTypes())
            Console.WriteLine("type name: " + type.FullName);
    }

    // 顯示指定型別所有方法
    public void DisplayMethods() {
        Console.WriteLine("display methods in TestReflection Type ...");
        Assembly assembly = Assembly.LoadFrom(file);
        Type type = assembly.GetType("TestReflection");
        foreach (MethodInfo method in type.GetMethods())
            Console.WriteLine("method name: " + method.Name);
    }

    // 調用靜態方法
    public void InvokeStaticMethod() {
        Console.WriteLine("Invoke static method ...");
        Assembly assembly = Assembly.LoadFrom(file);
        Type type = assembly.GetType("TestSubClass");
        type.InvokeMember("SayHello", BindingFlags.InvokeMethod, null, null, new object[] { });
    }

    // 調用實例方法
    public void InvokeMethod() {
        Console.WriteLine("Invoke Method ...");
        Assembly assembly = Assembly.LoadFrom(file);
        Type type = assembly.GetType("TestSubClass");
        object obj = Activator.CreateInstance(type);
        TestClass tc = (TestClass)obj;
        type.InvokeMember("Test1", BindingFlags.InvokeMethod, null, tc, new object[] { });
        type.InvokeMember("Test2", BindingFlags.InvokeMethod, null, tc, new object[] { });
    }
}

public interface TestClass {
    void Test1();
    void Test2();
}

public class TestSubClass : TestClass {
    public void Test1() { Console.WriteLine("This is TestSubClass.Test1"); }
    public void Test2() { Console.WriteLine("This is TestSubClass.Test2"); }
    public static void SayHello() { Console.WriteLine("This is TestSubClass.SayHello"); }
}

執行輸出


display modules ...
module name: TestReflection.exe
display types ...
type name: TestReflection
type name: TestClass
type name: TestSubClass
display methods in TestReflection Type ...
method name: GetHashCode
method name: Equals
method name: ToString
method name: DisplayModules
method name: DisplayTypes
method name: DisplayMethods
method name: InvokeStaticMethod
method name: InvokeMethod
method name: Test2
method name: GetType
Invoke static method ...
This is TestSubClass.SayHello
Invoke Method ...
This is TestSubClass.Test1
This is TestSubClass.Test2

關鍵 API 解析

Assembly.LoadFrom vs Load

方法用途
Assembly.LoadFrom(path)從路徑載入,支援相對路徑,載入上下文為 LoadFrom
Assembly.Load(name)從 GAC 或探索路徑載入,載入上下文為 Load
Assembly.ReflectionOnlyLoadFrom(path)僅檢視中繼資料,不可執行程式碼

Type.GetMethods 回傳順序

包含繼承自 Object 的方法(GetHashCode、Equals、ToString、GetType),順序不保證,勿依賴順序邏輯。

InvokeMember 綁定旗標


// 靜態方法
type.InvokeMember("SayHello", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, null, null, args);

// 實例方法
type.InvokeMember("Test1", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, instance, args);

// 非公開成員
BindingFlags.NonPublic | BindingFlags.Instance

// 忽略大小寫
BindingFlags.IgnoreCase

Activator.CreateInstance 多載


// 無參數建構子
object obj = Activator.CreateInstance(type);

// 有參數建構子
object obj = Activator.CreateInstance(type, arg1, arg2);

// 指定建構子綁定旗標
object obj = Activator.CreateInstance(type, BindingFlags.NonPublic | BindingFlags.Instance, null, args, null);

效能考量與最佳化

技術相對效能適用場景
直接呼叫1x (基準)已知型別、高頻呼叫
委派快取~1.5x重複調用同一方法
MethodInfo.Invoke~50-100x動態型別、低頻
動態方法~2-5x高頻動態調用、IL 生成
表達式樹編譯~5-10xLambda 編譯、LINQ Provider

委派快取模式


private static readonly Dictionary> _methodCache = new();

public object InvokeCached(object target, string methodName, params object[] args) {
    var key = $"{target.GetType().FullName}.{methodName}";
    if (!_methodCache.TryGetValue(key, out var func)) {
        var method = target.GetType().GetMethod(methodName);
        func = (instance, parameters) => method.Invoke(instance, parameters);
        _methodCache[key] = func;
    }
    return func(target, args);
}

安全性與權限

  • 反射需 ReflectionPermission(全信任環境預設滿足)
  • 存取非公開成員需 ReflectionPermissionFlag.MemberAccess
  • 部分信任環境(ClickOnce、Silverlight)受限
  • .NET Core / .NET 5+ 精簡反射 API,部分功能需 System.Reflection.Metadata

總結

反射是 C# 動態能力的核心,支援外掛、框架、工具開發。關鍵在於平衡靈活性與效能:熱路徑使用委派快取或原始碼生成,冷路徑直用 MethodInfo.Invoke。掌握 BindingFlags 組合、建構子選擇、泛型方法構造(MakeGenericMethod)是進階反射必備技能。

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