系列第三篇聚焦 C# Attribute 與 Java Annotation 的本質差異。C# 1.0 即擁有 Attribute,Java 5.0 才引入 Annotation,且在設計哲學上存在根本缺陷。
C# Attribute 是類,繼承 System.Attribute,支援繼承、抽象方法、多態、介面實現等完整 OO 機制。Java Annotation 是介面,隱式繼承 java.lang.annotation.Annotation,僅能定義無參方法(類字段),無法包含邏輯。
public abstract class ValidationAttribute : Attribute {
public abstract ValidationResult Validate(object value);
}
public class RangeAttribute : ValidationAttribute {
public int Min { get; set; }
public int Max { get; set; }
public override ValidationResult Validate(object value) { ... }
}
public class Person {
[Range(Min=10, Max=60)] public int Age { get; set; }
[Regex(Pattern=@"^\w+@...")] public string Email { get; set; }
}
// 統一驗證邏輯
foreach (var prop in type.GetProperties()) {
var attrs = (ValidationAttribute[])prop.GetCustomAttributes(typeof(ValidationAttribute), true);
foreach (var attr in attrs) attr.Validate(propValue);
}
基類 ValidationAttribute 定義統一契約,擴展只需繼承並實現 Validate,框架零修改支援新驗證規則。
public @interface RangeValidation { int min(); int max(); }
public @interface RegexValidation { String pattern(); }
// 驗證時必須針對每種 Annotation 寫死邏輯
if (annot instanceof RangeValidation) { ... }
else if (annot instanceof RegexValidation) { ... }
// 無法統一處理,擴展需修改框架代碼
Annotation 只是「死數據」,無法攜帶行為,導致框架與業務邏輯緊耦合,違反開閉原則。
C# Attribute 透過建構函數天然支援多重載、可選參數、封裝性:
[Range(10, 60)] // 建構函數注入
[Regex(@"^...$")] // 單參建構函數,Pattern 唯讀
[AttributeUsage(AttributeTargets.Property, AllowMultiple=true)] // 混合建構函數+屬性
Java 強制要求單一 value() 方法才能省略屬性名:
@RegexValidation("^...$") // 必須將方法命名為 value,無法命名 pattern
// 多參數無法省略屬性名,單參數也必須改名 value
這種人為限制降低了 API 表達力,違反最小驚訝原則。
enum 強類型常量(Java 版本更豐富)...(對應 C# params)foreach)結論:C# Attribute 設計更符合「萬物皆物件」,支援豐富編程模式;Java Annotation 受限於介面本質,淪為配置數據載體,錯失框架級擴展機會。