反射技術與設計模式
.NET 可執行程式結構層級
AppDomain → Assembly → Module → Type → Member。反射封裝各層級物件,支援運行時:
- 載入組件、列舉型別、建立實例
- 檢視/調用方法、屬性、欄位、事件、建構子
- 讀取自訂屬性
反射核心 API 對照表
| 目標 | 反射類別 | 常用方法 |
| 組件 | Assembly | LoadFrom, GetTypes, CreateInstance |
| 型別 | Type | GetMethods, GetProperties, GetConstructors |
| 方法 | MethodInfo | Invoke, GetParameters, ReturnType |
| 屬性 | PropertyInfo | GetValue, SetValue, PropertyType |
| 欄位 | FieldInfo | GetValue, SetValue, FieldType |
| 事件 | EventInfo | AddEventHandler, RemoveEventHandler |
| 建構子 | ConstructorInfo | Invoke, GetParameters |
| 參數 | ParameterInfo | Name, ParameterType, IsIn/IsOut |
反射範例:動態列舉、建立、調用
Assembly asm = Assembly.LoadFrom("ReflectionExample.exe");
foreach (Type t in asm.GetTypes()) Console.WriteLine(t.Name);
Type helloType = typeof(HelloWorld);
foreach (MethodInfo mi in helloType.GetMethods()) Console.WriteLine(mi.Name);
object obj = Activator.CreateInstance(helloType);
object objWithArg = Activator.CreateInstance(helloType, new object[] { "zhenlei" });
MethodInfo sayHello = helloType.GetMethod("SayHello");
sayHello.Invoke(obj, null);
sayHello.Invoke(objWithArg, null);
反射簡化設計模式實作
| 模式 | 傳統痛點 | 反射優化 |
| 工廠方法 | 每新增產品類別需修改工廠代碼 | 配置檔存儲型別名,工廠通用 Activator.CreateInstance |
| 抽象工廠 | 產品族擴充需新增多個工廠子類 | 反射動態組裝產品族,減少子類爆炸 |
| 命令模式 | 命令類別與調用者強耦合 | 以命令型別名為鍵動態建立並執行 |
| 享元模式 | 享元工廠需管理大量具體類別 | 反射按需實例化,簡化工廠邏輯 |
委派技術與設計模式
.NET 委派本質
- 型別安全的函數指標,繼承自
MulticastDelegate。
- 封裝靜態/實例方法,支援多播(調用列表)。
- 核心:
Invoke(同步)、BeginInvoke/EndInvoke(非同步 APM 模式)。
- 與 C++ 函數指標異:面向物件、型別安全、支援實例方法(隱含
this)。
委派聲明與使用
// 聲明
delegate float CompareFunc(float a, float b);
// 使用
public class TemplateProcessor
{
public CompareFunc Comparer { get; set; }
public float Process(float[] data)
{
float result = float.NaN;
foreach (var v in data)
result = float.IsNaN(result) ? v : Comparer(result, v);
return result;
}
}
// 客戶端動態綁定
var proc = new TemplateProcessor();
proc.Comparer = (a, b) => Math.Max(a, b); // Lambda 最大值
float max = proc.Process(new[] { 1f, 5f, 3f }); // 5
proc.Comparer = (a, b) => a + b; // 累加
float sum = proc.Process(new[] { 1f, 2f, 3f }); // 6
委派 vs GoF 委派模式
GoF 《設計模式》中的「委派」指物件組合:接受請求的物件將操作轉交給委派物件,並傳遞 this 使委派能回呼。.NET 委派更進一步:
- 解耦合:調用者完全不知曉目標方法所屬類別。
- 動態綁定:運行時決定綁定哪個方法,無需繼承體系。
- 多播:一個委派可串聯多個處理者(事件模型基礎)。
// GoF 風格:Window 持有 Rectangle 參考
window.Area = new Rectangle().Area; // 需引用 Rectangle 類別
// .NET 委派風格:Window 僅持有委派
public delegate float AreaCalculator();
public class Window { public AreaCalculator CalcArea; }
// 運行時綁定
var rect = new Rectangle();
var win = new Window();
win.CalcArea = new AreaCalculator(rect.CalculateArea); // 動態綁定,無編譯期依賴
委派簡化設計模式
| 模式 | 委派應用 |
| 模板方法 | 以委派注入變化步驟,替代繼承重寫,支援運行時組合 |
| 觀察者 | 事件本質即多播委派,event 關鍵字封裝加減訂閱 |
| 中介者 | 同事類透過委派與中介者通訊,移除雙向引用 |
| 策略 | 委派即輕量級策略介面,無需定義介面與實作類 |
綜合實戰:插件式規則引擎
// 規則介面(委派版)
public delegate bool RuleEvaluator(Context ctx);
// 規則載入器(反射版)
public class RuleLoader
{
public IEnumerable LoadRules(string assemblyPath)
{
Assembly asm = Assembly.LoadFrom(assemblyPath);
foreach (Type t in asm.GetTypes())
{
if (t.IsClass && !t.IsAbstract && typeof(IRule).IsAssignableFrom(t))
{
IRule rule = (IRule)Activator.CreateInstance(t);
yield return rule.Evaluate; // 方法組成委派
}
}
}
}
// 執行引擎
public class RuleEngine
{
private List _rules = new List();
public void AddRule(RuleEvaluator rule) => _rules.Add(rule);
public bool ExecuteAll(Context ctx) => _rules.All(r => r(ctx));
}
總結
- 反射解決「型別未知/動態載入/結構探查」問題,是框架級開發的利器。
- 委派解決「行為參數化/動態綁定/事件通知」問題,是業務邏輯組合的核心。
- 兩者結合,可構建高擴展、低耦合、配置驅動的系統架構。
- 濫用反射影響效能與可讀性,建議:框架層多用,業務層慎用;委派則大力推薦用於策略、模板、事件等場景。