提問者希望設計一個「巨集」,能將已定義類別的描述資訊(屬性讀寫類型、單位、閾值等)、以及指定 init、run、finalise 介面對應的具體函式,透過這個巨集來實現一套反射機制。
C# 本身就有完整、強大的內建反射機制(System.Reflection 命名空間),能在執行時動態:
typeof(MyClass)、obj.GetType()GetProperties()、GetFields()、GetMethods()prop.GetValue(obj)、prop.SetValue(obj, val)method.Invoke(obj, args)Attribute.GetCustomAttributes() 讀取掛在類別/屬性/方法上的中繼資料| 自行實現的弊端 | 內建反射的優勢 |
|---|---|
| C# 無前置處理器巨集,只能用原始碼產生器或 Roslyn,複雜度極高 | 零成本、編譯器原生支援、效能經過高度優化 |
| 需手動維護字串對應,易出錯、重構時失效 | 強型別、重構安全、IDE 智慧提示完整支援 |
| 序列化/反序列化、DI 容器、ORM 等生態皆建立在內建反射之上 | 生態系統完整,Json.NET、Entity Framework、Autofac 等開箱即用 |
| 效能差、除錯難、無法享受 JIT 優化 | 內部有快取、動態方法生成(DynamicMethod)、MethodInfo.CreateDelegate 等高效能路徑 |
若要在屬性上掛載「單位、閾值、讀寫類型」等中繼資料,請定義自訂 Attribute:
[AttributeUsage(AttributeTargets.Property)]
public class MetaAttribute : Attribute {
public string Unit { get; set; }
public double Threshold { get; set; }
public bool ReadOnly { get; set; }
// 其他需要的中繼資料...
}
public class DeviceConfig {
[Meta(Unit = "°C", Threshold = 100, ReadOnly = false)]
public double Temperature { get; set; }
[Meta(Unit = "kPa", Threshold = 500, ReadOnly = true)]
public double Pressure { get; set; }
}
執行時讀取:
var props = typeof(DeviceConfig).GetProperties();
foreach (var prop in props) {
var meta = prop.GetCustomAttribute();
if (meta != null) {
Console.WriteLine($"{prop.Name}: Unit={meta.Unit}, Threshold={meta.Threshold}");
}
}
不用巨集綁 init/run/finalise,定義介面:
public interface ILifecycle {
void Init();
void Run();
void Finalise();
}
public class MyProcessor : ILifecycle {
public void Init() { /* ... */ }
public void Run() { /* ... */ }
public void Finalise() { /* ... */ }
}
搭配 DI 容器(如 Microsoft.Extensions.DependencyInjection)註冊與解析,編譯期型別安全、重構無痛、測試容易。
別試圖在 C# 裡發明「反射巨集」。語言設計者已經幫你把最難、最通用、最高效的反射基礎設施寫好、測好、優化好、文件化了。你要做的只是:定義 Attribute 裝中繼資料、定義 Interface 表達契約、用 Reflection API 讀取與驅動。這才是 C# 開發者的正確姿勢。