Delphi 物件導向可見性規則有個獨特特性:同一單元 (unit) 內的所有類別互為「友元」,可互相存取 private 與 protected 成員。這導致一個經典技巧:在目標類別所在單元之外,宣告一個「空繼承類別」即可存取其 protected 成員。本文深入解析此機制原理、適用場景與風險。
// UnitA.pas
unit UnitA;
interface
type
TBase = class
private
FSecret: Integer;
protected
procedure ProtectedProc;
public
procedure PublicProc;
end;
implementation
procedure TBase.ProtectedProc;
begin
// ...
end;
end.
// UnitB.pas
unit UnitB;
interface
uses UnitA;
type
// 關鍵:在同一單元內宣告繼承類別
TBaseCracker = class(TBase); // 空繳承,無任何新增成員
TConsumer = class
public
procedure AccessBaseProtected(Base: TBase);
end;
implementation
procedure TConsumer.AccessBaseProtected(Base: TBase);
begin
// 合法!因為 TBaseCracker 與 TConsumer 同在 UnitB
// TBaseCracker 繼承 TBase,將 TBase 的 protected 成員「帶入」UnitB 可見範圍
TBaseCracker(Base).ProtectedProc; // 硬性轉型存取
end;
end.
type
// 宣告於使用者單元 (如 Unit1.pas)
TStringGridCracker = class(TStringGrid); // 空繼承
procedure TForm1.Button1Click(Sender: TObject);
begin
if TStringGridCracker(StringGrid1).RowCount > 5 then
TStringGridCracker(StringGrid1).DeleteRow(5); // 呼叫 protected 方法
end;
TStringGrid 定義於 Vcl.Grids 單元,其 DeleteRow 為 protectedUnit1) 無法直接存取 Vcl.Grids 內部的 protected 成員Unit1 宣告 TStringGridCracker = class(TStringGrid)TStringGridCracker 成為 Unit1 的類別,享有 Unit1 的友元權限TStringGrid 的 protected 成員在 TStringGridCracker 中可見TStringGridCracker(StringGrid1) 即可呼叫| 存取位置 | private | protected | public/published |
|---|---|---|---|
| 同一類別 | ✅ | ✅ | ✅ |
| 同一單元、不同類別 | ✅ (友元) | ✅ (友元) | ✅ |
| 子類別、同一單元 | ✅ | ✅ | ✅ |
| 子類別、不同單元 | ❌ | ✅ | ✅ |
| 外部單元、非子類別 | ❌ | ❌ | ✅ |
| 外部單元、空繼承類別轉型 | ❌ | ✅ (技巧) | ✅ |
private 成員無法透過繼承存取,但同單元友元規則仍適用:
// Unit1.pas
type
TMyClass = class
private
FValue: Integer;
end;
// 同單元內可直接存取 private
procedure HackPrivate(Obj: TMyClass);
begin
Obj.FValue := 99; // 合法!同單元友元
end;
跨單元存取 private 需用 RTTI (System.Rtti) 或 記憶體偏移 (不推薦,極不穩定)。
protected 虛擬方法覆寫行為protected 欄位/方法)protected 成員簽名變更導致編譯錯誤/執行錯誤protected 覆寫中呼叫 inherited,再公開新方法TRttiContext.GetType.Invoke 動態呼叫,不依賴編譯期類型Delphi 「同單元友元」特性配合空繼承類別,提供了繞過 protected 封裝的實用技巧。這既是語言特性的副作用,也是實務開發的急救工具。理解其原理、限制與風險,謹慎使用、優先尋找正規替代方案,才能在靈活性與可維護性間取得平衡。