This is a follow-up question to: How to hide a protected procedure of an object?
(I'm a bit fuzzy on the whole class helper concept)
Suppose I have an class like:
type
TShy = class(TObject)
strict private
procedure TopSecret;
private
procedure DirtyLaundry;
protected
procedure ResistantToChange;
end;
I know I can access the private method if I have the source code by adding a descendent class in the same unit.
I have 2 questions:
- How do I employ a class helper to access the strict private
member?
- Can I use a class helper in a separate unit to access (strict) private members?
Up to, and including Delphi 10.0 Seattle, you can use a class helper to access strict protected
and strict private
members, like this:
unit Shy;
interface
type
TShy = class(TObject)
strict private
procedure TopSecret;
private
procedure DirtyLaundry;
protected
procedure ResistantToChange;
end;
unit NotShy;
interface
uses
Shy;
type
TNotShy = class helper for TShy
public
procedure LetMeIn;
end;
implementation
procedure TNotShy.LetMeIn;
begin
Self.TopSecret;
Self.DirtyLaundry;
Self.ResistantToChange;
end;
end.
uses
..., Shy, NotShy;
procedure TestShy;
var
Shy: TShy;
begin
Shy := TShy.Create;
Shy.LetMeIn;
Shy.Free;
end;
However, starting with Delphi 10.1 Berlin, this no longer works! Class helpers can no longer access strict protected
, strict private
or private
members. This "feature" was actually a compiler bug that Embarcadero has now fixed in Berlin. You are out of luck.
Access to private
and strict private
members of a class with class helpers
was removed in Delphi 10.1 Berlin. See Closing the Class Helpers Private Access Loophole.
But there is still an open loophole:
unit Shy;
interface
type
TShy = class(TObject)
strict private
procedure TopSecret;
private
procedure DirtyLaundry;
protected
procedure ResistantToChange;
end;
implementation
procedure TShy.DirtyLaundry;
begin
WriteLn('DirtyLaundry');
end;
procedure TShy.ResistantToChange;
begin
WriteLn('ResistantToChange');
end;
procedure TShy.TopSecret;
begin
WriteLn('TopSecret');
end;
end.
Program TestClassHelpers;
{$APPTYPE CONSOLE}
Uses
Shy;
type
TNotShy = class helper for TShy
public
procedure LetMeIn;
end;
procedure TNotShy.LetMeIn;
var
P : procedure of object;
begin
TMethod(P).Code := @TShy.TopSecret;
TMethod(P).Data := Self;
P; // Call TopSecret
TMethod(P).Code := @TShy.DirtyLaundry;
TMethod(P).Data := Self;
P; // Call DirtyLaundry;
Self.ResistantToChange; // Protected access works without problems
end;
var
myObj: TShy;
begin
myObj := TShy.Create;
try
myObj.LetMeIn;
ReadLn;
finally
myObj.Free;
end;
end.