How do I use class helpers to access strict private members of a class?
Asked Answered
P

2

29

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?

Pallaton answered 23/2, 2012 at 9:42 Comment(1)
See also this SO question: access-a-strict-protected-property-of-a-delphi-classBoll
B
38

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.

Bemused answered 23/2, 2012 at 9:57 Comment(15)
JFTR: In D2007, the Self.DirtyLaundry; and Self.ResistantToChange; lines don't compile while Self.TopSecret; does. I.e. you have access to strict private members but not to private or protected members. :-DOutlive
Thanks, just to be clear on this, in D2007 the helper will only let me access strict private members like this, in Delphi 2010 and up the helper will let me access all class members. Correct?Pallaton
@Johan, I just copy&pasted Remy's code into a D2007 test project and had to comment out the DirtyLaundry and ResistantToChange lines. The TopSecret line compiled fine.Outlive
@Remy, I didn't want to imply otherwise. Some changes in this area seem to have happened, probably from D2007 to D2009.Outlive
This does not work anymore (for private members) in Delphi 10.1 Berlin as it has been classified as a bug in the compiler and was fixed.Trever
See RSB-1146 Fix for "Class helpers allow cross-unit private violations" breaks existing code and Marco's comments on RSB-1254 can't access to private fields in helpers, such as "[access to privates in helpers] was filed as a bug and rightfully so. R&D was not aware of the hack. We thought about leaving the bug open, but decided against. It is too much of a fundamental problem".Bemused
@Johan: both links work fine for me. Though now that Berlin is released, and all RSB tickets will soon be moved to RSP, the links may or may not break. I'll update them if they do.Bemused
You say: strict protected and strict private what about private? That one is also verboten in Berlin right?Pallaton
@Johan: This question talks about strict access, but yes, in general private data cannot be accessed outside of its declaring unit. The compiler now enforces this for class helpers. Embarcadero considered class helpers accessing private data to be a bug, and the bug has now been fixed and will not be reversed (despite people asking for it).Bemused
And if you define private method in your class helper you also cannot access it in your own code. Nice...Windflower
This is total mistake to remove code worked through xe2, xe3, xe4, xe5, ex6, xe7, xe8, 10 - really nice move - but when they loos money because many people forgot to upgrade to newer version - then maybe they chenge decision. This is the same like car 200km/h but in new version it can only drive 30km/h - mayby someone buy it but my company not. If you need back this feature then vote on quality.embarcadero.com/browse/RSP-14711Huysmans
All indications are this "feature" (bug) is gone now and is never coming back. I depend on it too, and I'm left without a reasonable alternative. RTTI is my current best "unreasonable" alternative, followed by code hooking, and evil assembler hacks. Your link is dead now (closed as duplicate). Current discussion is at: quality.embarcadero.com/browse/RSP-14347Bhang
Another reason to move to Lazarus entirely and once for good!Inconsequent
Access to strict protected members is still allowed.Boll
Current "open" proposition on QualityCentral is: quality.embarcadero.com/browse/RSP-15273 but still not fixed - you can vote for fix about access to private fieldsHuysmans
B
8

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.
Boll answered 11/6, 2016 at 8:26 Comment(3)
You just made my day. That's brilliant.Pallaton
I'm afraid this doesn't replace the old "feature" completely, because it only gives you access to methods, not fields. Before Delphi 10.1 Berlin we could also access private fields of that class.Troop
For a way to access private fields, see How to access private methods without helpers?. By using a combination of class helpers and RTTI, it is possible to retain the performance.Boll

© 2022 - 2024 — McMap. All rights reserved.