__property 擴展詳解:自定義屬性完整指南Borland C++ Builder 引入 __property 關鍵字,將 Delphi 風格的「屬性」語法帶入 C++,讓類成員訪問兼具字段般簡潔與方法般封裝的雙重優勢。本文系統講解簡單屬性、陣列屬性、唯讀屬性、預設屬性、存取指定符,並針對商業級開發提出異常安全與IDE 整合最佳實踐。
__property <類型> <名稱> [<索引列表>]
= { read=<讀取器>, write=<寫入器>, stored=<布爾/方法>, default=<值>, nodefault, index=<整數> };
read / write:必填至少一個,對應成員函數或字段。stored:決定是否序列化至 .dfm,預設 true。default / nodefault:指定預設值,IDE 物件檢查器以此決定是否顯示粗體。index:多屬性共用同一讀寫器時的區分標識。class TSome {
protected:
int a, b, s;
int GetSum() { return s = a + b; } // 讀取器
void SetSum(int val) { s = val; } // 寫入器
public:
__property int Sum = { read=GetSum, write=SetSum, nodefault };
};
使用:obj.Sum = 10; → 呼叫 SetSum(10);int v = obj.Sum; → 呼叫 GetSum()。
class TSome {
private:
TList *list;
protected:
void* GetItem(int i) {
return (i >= 0 && i < list->Count) ? list->Items[i] : nullptr;
}
void SetItem(int i, void* ptr) {
if (i >= 0 && i < list->Count) list->Items[i] = ptr;
else if (i == list->Count) list->Add(ptr); // 允許尾部擴充
}
int GetCount() { return list->Count; }
public:
TSome() : list(new TList) {}
~TSome() { delete list; }
__property void* Items[int i] = { read=GetItem, write=SetItem };
__property int Count = { read=GetCount, nodefault }; // 唯讀
};
使用:obj.Items[3] = ptr; → SetItem(3, ptr);int n = obj.Count; → GetCount()。
| 模式 | 語法示例 | 用途 |
|---|---|---|
| 預設屬性 | __property void* Items[int i] = { ... }; // 類外可 obj[i] | 類似 std::vector::operator[] |
| 索引屬性共用器 | __property int Prop1 = { read=GetVal, write=SetVal, index=1 }; | 單一方法處理多屬性 |
| 存取控制 | __property int X = { read=GetX, write=SetX, protected }; | 限制繼承類可見性 |
| 屬性別名 | __property int Alias = { read=GetReal, write=SetReal, index=0 }; | 向後相容重命名 |
Object Inspector、DFM 流式讀寫、Assign() 複製等任意時機。void SetName(const AnsiString& s) {
if (s.IsEmpty()) throw EArgumentException("Name cannot be empty");
FName = s; // AnsiString 賦值不拋異常
}
❌ 錯誤:在寫入器中直接拋出未捕獲的異常導致 IDE 崩潰或 DFM 載入失敗。stored 動態判斷bool __fastcall IsNameStored() { return FName != DefaultName; }
__property AnsiString Name = { read=GetName, write=SetName, stored=IsNameStored };
避免將預設值寫入 DFM,減小表單檔案體積。nodefault 選擇default=0;字串/物件/浮點數務必用 nodefault,避免浮點比較陷阱。TPropertyEditor 子類並 RegisterPropertyEditor(),否則 Object Inspector 僅顯示原始值。getter/setter 命名慣例 + 宏條件編譯隔離。善用 __property,你的 VCL 元件將擁有原生級的設計時體驗;配合異常安全守則,更能讓商業級元件在 IDE 中穩如磐石。