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

C# 反射動態調用成員完整指南:InvokeMember 參數詳解與實戰模式

     編輯:關於C語言

InvokeMember 簽名解析


public object InvokeMember(
    string name,                    // 成員名稱,空字串表示預設成員
    BindingFlags invokeAttr,        // 綁定旗標組合
    Binder binder,                  // 自訂繫結器,通常傳 null 使用預設
    object target,                  // 目標物件(靜態成員傳 null)
    object[] args                   // 參數陣列
);

BindingFlags 完整分類

篩選旗標(選擇哪些成員參與)

旗標用途
Public包含公用成員
NonPublic包含非公用成員(private/protected/internal)
Static包含靜態成員
Instance包含實例成員
FlattenHierarchy包含繼承鏈上的靜態成員
DeclaredOnly僅搜尋直接宣告於該型別的成員,不含繼承
IgnoreCase名稱比對忽略大小寫

調用旗標(指定執行什麼操作)

旗標操作互斥旗標
InvokeMethod調用方法CreateInstance, GetField, SetField, GetProperty, SetProperty
CreateInstance呼叫建構子其他調用旗標
GetField取得欄位值SetField
SetField設定欄位值GetField
GetProperty取得屬性值SetProperty
SetProperty設定屬性值GetProperty

完整實戰範例


using System;
using System.Reflection;

namespace ConsoleApplication9 {
    public class Love {
        public int field1;
        private string _name;
        public string Name {
            get => _name;
            set => _name = value;
        }
        public int GetInt(int a) => a;
        public void Display(string str) => Console.WriteLine(str);
    }

    class Program {
        static void Main() {
            Love love = new Love();
            Type type = love.GetType();

            // 1. 動態建立實例(等同 new Love())
            object obj = type.InvokeMember(
                null,
                BindingFlags.CreateInstance | BindingFlags.Public | BindingFlags.Instance |
                BindingFlags.DeclaredOnly,
                null, null, null);

            // 2. 調用無回傳值方法
            type.InvokeMember("Display",
                BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance,
                null, obj, new object[] { "Hello Reflection" });

            // 3. 調用有回傳值方法
            int result = (int)type.InvokeMember("GetInt",
                BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance,
                null, obj, new object[] { 42 });
            Console.WriteLine($"GetInt 回傳: {result}");

            // 4. 設定屬性
            type.InvokeMember("Name",
                BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance,
                null, obj, new object[] { "Dynamic Name" });

            // 5. 取得屬性
            string name = (string)type.InvokeMember("Name",
                BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance,
                null, obj, null);
            Console.WriteLine($"Name 屬性: {name}");

            // 6. 設定欄位(注意大小寫:field1 非 Field1)
            type.InvokeMember("field1",
                BindingFlags.SetField | BindingFlags.Public | BindingFlags.Instance,
                null, obj, new object[] { 999 });

            // 7. 取得欄位
            int fieldVal = (int)type.InvokeMember("field1",
                BindingFlags.GetField | BindingFlags.Public | BindingFlags.Instance,
                null, obj, null);
            Console.WriteLine($"field1 欄位: {fieldVal}");

            Console.ReadLine();
        }
    }
}

關鍵實戰模式

模式 1:快取 MemberInfo 避免重複反射


private static readonly Dictionary _methodCache = new();

public object CallMethod(object target, string methodName, params object[] args) {
    var key = $"{target.GetType().FullName}.{methodName}";
    if (!_methodCache.TryGetValue(key, out var method)) {
        method = target.GetType().GetMethod(methodName,
            BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
        _methodCache[key] = method;
    }
    return method.Invoke(target, args);
}

模式 2:泛型方法動態構造


MethodInfo generic = typeof(Repository).GetMethod("GetById");
MethodInfo constructed = generic.MakeGenericMethod(entityType);
object result = constructed.Invoke(repoInstance, new object[] { id });

模式 3:非公開建構子實例化


ConstructorInfo ctor = type.GetConstructor(
    BindingFlags.NonPublic | BindingFlags.Instance,
    null, Type.EmptyTypes, null);
object instance = ctor.Invoke(null);

模式 4:索引子/預設屬性存取


// 取得 this[int index]
object item = type.InvokeMember("Item",  // 或空字串 ""
    BindingFlags.GetProperty | BindingFlags.Instance,
    null, target, new object[] { index });

效能基準參考

呼叫方式相對耗時備註
直接呼叫1x基準
Delegate 快取1.2x推薦高頻動態調用
MethodInfo.Invoke50-100x低頻、啟動期可接受
InvokeMember80-150x含名稱查找開銷
動態方法3-5xIL 發射、適合熱路徑

常見錯誤與除錯

  • MissingMethodException:BindingFlags 漏掉 NonPublic、Static/Instance、參數型別不匹配
  • TargetException:靜態成員傳了 target、實例成員傳 null
  • ArgumentException:參數數量/型別不符、ByRef 參數處理錯誤
  • 大小寫敏感:預設區分大小寫,需加 IgnoreCase
  • 欄位/屬性名稱衝突:同名欄位與屬性需用 GetField/GetProperty 明確區分

總結

InvokeMember 是反射的「瑞士刀」,單一 API 涵蓋建構、方法調用、屬性/欄位存取。掌握 BindingFlags 組合邏輯是關鍵。高效能場景請改用 MethodInfo/PropertyInfo 快取或動態方法。動態調用威力大但責任大:編譯期失去型別安全,務必配合單元測試與契約驗證。

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