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

Viewed 109

The question may be a bit confusing, and is best illustrated by an example:

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;

Notice in the implementation section that there is a type TcxSplitterAccess being declared as a child of the TcxSplitter class. In the method SomeMethod(), belonging to the class TestClass, a TcxSplitter object is cast to the locally-declared TcxSplitterAccess class, and then a protected field is accessed on that object.

This is surprising to me as someone coming from a background of languages like Java, C++, C#, etc. In those languages, it is possible to access protected data in an object so long as you are doing it from within that object's type or an inherited type. For example, a method inside of a class ClazzA can access the private fields of other ClazzA objects since access is enforced at the type level rather than the instance level. Declaring a class locally in these languages would not give the containing class access to the local class' protected data (edit: As pointed out in the comments, this is actually not true at least for Java).

In this example, however, the type TestClass is directly accessing a protected field on the TcxSplitter object by first casting to the TcxSplitterAccess type. I am having trouble finding documentation on why this "trick" works. Does Delphi handle access levels fundamentally differently to Java-like languages and allows this sort of thing? Regardless, why does this trick work?

While I stumbled onto this behavior by using a nested, inherited class in order to access fields on the parent class (which breaks encapsulation, and I shouldn't do), the use of inheritance here is unnecessary. If the nested class did not inherit from a class, but instead had its own protected fields defined, TestClass would still be able to access those protected fields.

1 Answers

A unit has implicit-friendship semantics within itself. Types declared in the same unit are "friends" of each other (similar to friend in C++), and so can access each other's private and protected members (but not strict private or strict protected members).

So, in this case:

  • TcxSplitterAccess derives from TcxSplitter, so TcxSplitterAccess inherits all of the protected (but not private) members of TcxSplitter via normal class inheritance.
  • TestClass is declared in the same unit as TcxSplitterAccess, so TestClass has access to all of the protected members of TcxSplitterAccess, including the protected members of TcxSplitter (and the protected members of its ancestors).
  • FPositionBeforeClose is protected in TcxSplitter.

So, that is why TestClass.SomeMethod() is able to access FPositionBeforeClose when type-casting the Splitter1 object to TcxSplitterAccess.

This is covered by the Delphi documentation:

Private, Protected, Public, and Published Declarations

Classes and Objects (Delphi): Visibility of Class Members

Related