This question arose from an issue that surfaced when using method chaining (fluent interface), and I suppose that's one of the only reasons it might be an issue at all.
To illustrate, I'll use an example using method chaining:
In unit A:
TParent = class
protected
function DoSomething: TParent;
end;
In unit B:
TChild = class(TParent)
public
procedure DoAnotherThing;
end;
implementation
procedure TChild.DoAnotherThing;
begin
DoSomething.DoSomething
end;
I want to keep the DoSomething procedure protected and visible only to class descendants.
This won't compile, throwing a
cannot access protected symbol TParent.DoSomething
because DoSomething returns a TParent and the subsequent DoSomething call is issued from a TParent object in another unit (so the protection kicks in and the function is inaccessible). (thanks, David Heffernan, for the explanation)
To reduce it to its bare essence, something like TParent(Self).DoSomething is not possible inside the TChild class.
My question:
since the compiler does know that a copy of the Self parameter is being accessed from within the child class, would there be instances in which the ability to access the ancestor's protected methods breaks encapsulation? I'm only talking about dereferencing the typecasted Self from inside a descendant's class method. I'm aware that outside of this class, that parameter should not have access to the ancestor's protected methods (in another unit), of course.
Again, in short: when a variable, that is identical to the Self parameter, is dereferenced INSIDE one of its own class methods, would it be unsafe for the compiler to allow it access to its parent's protected methods (just like the Self parameter itself)?
It's a pretty theoretical question, but I'd be interested if it would have any negative impact on compiled code or encapsulation, if the compiler would allow this.
Thanks.
protected
means that you have access to those methods in your own instance. It does not mean that you have access to those methods on any other instances which are of a type you are deriving from.
The reason you can call DoSomething
in TChild
is because only Self
has access to protected members of its ancestors.
The fact that in this particular case the result of the DoSomething
method equals Self
cannot be evaluated by the compiler (except by some static code analysis which I doubt any OOP language compiler out there does).
C++ solves this with being able to make TChild a friend class of TParent and thus giving it access to those methods.
In Delphi you get that feature if you put both classes into the same unit. If you want to keep both classes in their own units you can still use that feature by declaring a "cracker class". Just make a class that inherits from TParent
and put that into the same unit as TChild
. Then you can cast the result of DoSomething
to that class and access the protected methods of TParent
.
type
TChild = class(TParent)
public
procedure DoAnotherThing;
end;
implementation
type
TParentAccess = class(TParent);
procedure TChild.DoAnotherThing;
begin
TParentAccess(DoSomething).DoSomething;
end;
Update 6.12.2017:
You can also add a method to your TChild
class to mimic the "friend status" (thanks Ken Bourassa).
type
TChild = class(TParent)
private
type
TParentAccess = class(TParent);
function DoSomething: TParentAccess; inline;
public
procedure DoAnotherThing;
end;
implementation
function TChild.DoSomething: TParentAccess;
begin
Result := TParentAccess(inherited DoSomething);
end;
procedure TChild.DoAnotherThing;
begin
DoSomething.DoSomething;
end;
A third possibility would be using a class helper to make the method accessible. This has the benefit of easy reusability as you just need to add the helper unit to any unit where you have a child of TParent
and need access.
type
TParentHelper = class helper for TParent
public
function DoSomething: TParent; inline;
end;
implementation
function TParentHelper.DoSomething: TParent;
begin
Result := inherited DoSomething;
end;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With