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.Invoke | 50-100x | 低頻、啟動期可接受 |
| InvokeMember | 80-150x | 含名稱查找開銷 |
| 動態方法 | 3-5x | IL 發射、適合熱路徑 |
常見錯誤與除錯
- MissingMethodException:BindingFlags 漏掉
NonPublic、Static/Instance、參數型別不匹配
- TargetException:靜態成員傳了 target、實例成員傳 null
- ArgumentException:參數數量/型別不符、ByRef 參數處理錯誤
- 大小寫敏感:預設區分大小寫,需加
IgnoreCase
- 欄位/屬性名稱衝突:同名欄位與屬性需用
GetField/GetProperty 明確區分
總結
InvokeMember 是反射的「瑞士刀」,單一 API 涵蓋建構、方法調用、屬性/欄位存取。掌握 BindingFlags 組合邏輯是關鍵。高效能場景請改用 MethodInfo/PropertyInfo 快取或動態方法。動態調用威力大但責任大:編譯期失去型別安全,務必配合單元測試與契約驗證。