反射 是 .NET 在執行時檢查、操作型別中繼資料的機制。核心命名空間:System.Reflection。主要類別:
Assembly:組件(.dll/.exe),載入、查詢型別、建立實例Type:型別資訊,成員(方法、屬性、欄位、事件、建構函式)MethodInfo / PropertyInfo / FieldInfo / ConstructorInfo:具體成員操作Activator:動態建立實例| 概念 | 說明 |
|---|---|
| 命名空間 | 邏輯分組(如 System.Collections.Generic),編譯時概念,不影響部署 |
| 組件 | 物理部署單位(.dll/.exe),執行時載入單位,包含型別實作 |
關鍵:一個組件可包含多個命名空間;一個命名空間可跨多個組件。反射查詢型別必須指定組件(mscorlib.dll 除外)。
// 1. 從實例取得(最常用)
Type t1 = "hello".GetType();
// 2. 從型別名稱取得(需完整限定名稱 + 組件名)
Type t2 = Type.GetType("System.Data.DataTable, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
// 3. 從組件取得
Assembly asm = Assembly.Load("System.Data");
Type t3 = asm.GetType("System.Data.DataTable");
// 4. 編譯時已知型別用 typeof(效能最佳)
Type t4 = typeof(string);
// 無參數建構函式
Type t = Type.GetType("System.Data.DataTable, System.Data");
DataTable dt = (DataTable)Activator.CreateInstance(t);
// 有參數建構函式
Type t2 = typeof(TestClass); // 假設 public TestClass(string name)
object[] args = { "Timmy" };
TestClass obj = (TestClass)Activator.CreateInstance(t2, args);
// 泛型版本(.NET Core 3.0+)
T instance = Activator.CreateInstance<T>();
public class TestClass
{
public string GetValue(string prefix) => $"{prefix} : {_value}";
public string Value { get; set; }
private string _value;
}
// 取得 MethodInfo
MethodInfo method = t.GetMethod("GetValue"); // 公開實例方法
// 或精確繫結
MethodInfo method = t.GetMethod("GetValue", BindingFlags.Public | BindingFlags.Instance, null, new[] { typeof(string) }, null);
// 調用
object[] parameters = { "Hello" };
object result = method.Invoke(obj, parameters); // 回傳 object,需轉型
Console.WriteLine(result); // "Hello : Timmy"
// 設定屬性
PropertyInfo prop = t.GetProperty("Value");
prop.SetValue(obj, "New Value");
// 讀取屬性
string val = (string)prop.GetValue(obj);
// 欄位(含私有)
FieldInfo field = t.GetField("_value", BindingFlags.NonPublic | BindingFlags.Instance);
field.SetValue(obj, "Direct Field Access");
MethodInfo.Invoke 有效能開銷。頻繁調用時建立委派:
// 委派型別
delegate string TestDelegate(string value);
// 建立委派(.NET 2.0+)
TestDelegate del = (TestDelegate)Delegate.CreateDelegate(
typeof(TestDelegate), obj, "GetValue");
// 直接調用(接近直接呼叫效能)
string result = del("Hello");
.NET Core 3.0+ 更推薦 MethodInfo.CreateDelegate<T>():
var del = method.CreateDelegate<Func<string, string>>(obj);
string result = del("Hello");
// 1. 定義介面契約(共用組件)
public interface IPlugin
{
string Name { get; }
void Execute();
}
// 2. 主程式動態載入外掛
public class PluginLoader
{
public IEnumerable<IPlugin> LoadPlugins(string pluginsPath)
{
foreach (string dll in Directory.GetFiles(pluginsPath, "*.dll"))
{
Assembly asm = Assembly.LoadFrom(dll);
foreach (Type t in asm.GetTypes())
{
if (typeof(IPlugin).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract)
{
yield return (IPlugin)Activator.CreateInstance(t);
}
}
}
}
}
// 3. 外掛實作(獨立組件)
public class LoggerPlugin : IPlugin
{
public string Name => "Logger";
public void Execute() => Console.WriteLine("Logging...");
}
| 操作 | 效能建議 |
|---|---|
| 頻繁反射調用 | 快取 Type/MethodInfo、建立委派、使用 Expression 編譯 Lambda |
| 熱路徑 | 避免反射,改用原始碼產生器、IL.Emit、DynamicMethod |
| AOT/Trimming 相容 | 標註 [DynamicallyAccessedMembers]、保留中繼資料、偏好介面/原始碼產生器 |
| 安全性 | 避免 BindingFlags.NonPublic 破壞封裝、沙箱環境限制反射權限 |