为什么嵌套 class 可以让包含的 class 访问子 class 的受保护数据?

Why does nesting a class give the containing class access to the child class' protected data?

这个问题可能有点令人困惑,最好用一个例子来说明:

unit Test
interface
type
  TestClass = class()
    Splitter1: TcxSplitter;
    procedure SomeMethod();
  end;

implementation
uses
  cxSplitter;

// Locally-declared child type
type
  TcxSplitterAccess = class(TcxSplitter);

procedure TestClass.SomeMethod()
var
  pos: integer;
begin
  // Access to protected field FPositionBeforeClose by casting
  pos := TcxSplitterAccess(Splitter1).FPositionBeforeClose;
end;

注意在实现部分有一个类型 TcxSplitterAccess 被声明为 TcxSplitter class 的子类型。在属于classTestClass的方法SomeMethod()中,将一个TcxSplitter对象强制转换为本地声明的TcxSplitterAccessclass,然后在该对象上访问受保护的字段。

对于具有 Java、C++、C# 等语言背景的人来说,这让我感到惊讶。在这些语言中,只要您是,就可以访问对象中受保护的数据从 在该对象的类型或继承类型 中进行。例如,class ClazzA 中的方法可以访问其他 ClazzA 对象的私有字段,因为访问是在 type 级别强制执行的,而不是比 实例 水平。在这些语言中在本地声明 class 不会给予包含 class 访问本地 class' 受保护数据的权限(edit: 正如在评论,这实际上不是真的,至少 Java).

然而,在此示例中,类型 TestClass 通过首先转换为 TcxSplitterAccess 类型直接访问 TcxSplitter 对象上的受保护字段。我无法找到有关此“技巧”为何起作用的文档。 Delphi 处理访问级别的方式是否与类似 Java 的语言有根本不同并允许这种事情?无论如何,为什么这个技巧会奏效?

虽然我通过使用嵌套的、继承的 class 来访问父 class 上的字段(这破坏了封装,我不应该这样做)而偶然发现了这种行为,但使用这里的继承是不必要的。如果嵌套 class 没有继承自 class,而是定义了自己的受保护字段,TestClass 仍然可以访问这些受保护字段。

A unit 本身具有 implicit-friendship 语义。在同一个 unit 中声明的类型 是彼此的“朋友”(类似于 C++ 中的 friend),因此可以访问彼此的 privateprotected 名成员(但不是 strict privatestrict protected 名成员)。

因此,在这种情况下:

  • TcxSplitterAccess 派生自 TcxSplitter,因此 TcxSplitterAccess 继承 TcxSplitter 的所有 protected(但不是 private)成员,通过正常 class 继承。
  • TestClass 在与 TcxSplitterAccess 相同的 unit 中声明,因此 TestClass 可以访问 [=17= 的所有 protected 成员], 包括 TcxSplitterprotected 成员(及其祖先的 protected 成员)。
  • FPositionBeforeCloseprotectedTcxSplitter.

所以,这就是为什么 TestClass.SomeMethod() 能够在 type-casting 将 Splitter1 对象指向 TcxSplitterAccess 时访问 FPositionBeforeClose

Delphi 文档对此进行了介绍:

Private, Protected, Public, and Published Declarations

Classes and Objects (Delphi): Visibility of Class Members