2

Given

type
  TMyClass = class
  private
    FPrivateInt : Integer;
  protected
    FProtectedInt : Integer;
  public
    FPublicInt : Integer;
  end;

in one unit and

type
  TMyHelper = class helper for TMyClass
    function Sum : Integer;
  end;
[...]
function TMyHelper.Sum: Integer;
begin
  Result := 0;
  Result := Result + FPublicInt;
  Result := Result + FProtectedInt;
  Result := Result + FPrivateInt;  // <- compiler error here
end;

in another, the XE8 compiler reports error "E2003 undeclared identifier 'FPrivateInt'. This is what I would intuitively have expected, given the restricted visibility of private members outside the unit where a class is declared, if I hadn't seen the example on p89/90 of Marco Cantu's Delphi 2007 Handbook of a class helper which accesses private fields of the "helped" class and also an unequivocal statement in the opening paragraph of the accepted answer to this q

Can I call static private class method with class helper?

which seems to support it: "As is widely known, helpers do crack private visibility. So, private members are visible from a class helper. ..."

So, why do I get the E2003 Undeclared Identifier error? I am obviously missing something somewhere, in my understanding or code. I get the same error using XE4 and XE6, btw, and XE4 pre-dates the SO answer I've referenced, which is from last year.

Community
  • 1
  • 1
MartynA
  • 30,454
  • 4
  • 32
  • 73
  • You could access private methods, not fields. – TLama Aug 12 '15 at 18:04
  • @TLama: Marco Cantu's example definitely uses fields. I have it in front of me. – MartynA Aug 12 '15 at 18:07
  • Then they must have fix this *feature* as yet in Delphi 2009 you cannot access fields that way. – TLama Aug 12 '15 at 18:08
  • The *authorative source* you cite is simply a high-rep SO user. With that being said, the question you linked asked about a *static private class method* (note **method**, not **field**). The example Marco shows uses *Value* (a private field), but he also qualifies it as being defined in the same unit (see the second paragraph after the `procedure TMyObjectHelper.Show` code block which begins *Of course, it makes very little sense to declare a class and an extension to the same class...in the same unit*, which the code you're citing as an example does. – Ken White Aug 12 '15 at 18:20
  • @KenWhite: Thanks, I've qualified my reference to the previous SO answer. I get what you're saying about what the other *question* is about, but the second sentence of the *answer* there refers to private members, fwiw. – MartynA Aug 12 '15 at 18:25
  • 1
    It may use the word *members*, but all code there (and the intent of the text) demonstrates use with private methods. Also look at all of the quoted documentation in LURD's answer to that same question, all of which refers to private *methods* or *functions*. – Ken White Aug 12 '15 at 18:48
  • I think the version with "authoritative source" is better! ;-) – David Heffernan Aug 12 '15 at 18:56
  • 1
    @DavidHeffernan: Arf. I changed it to spare blushes, of course. – MartynA Aug 12 '15 at 19:01
  • 1
    @Ken In case you missed the resolution to this, note that the use of the word members was accurate and intentional. Private fields and just as visible as private methods in a helper. – David Heffernan Aug 13 '15 at 06:33
  • @David: I saw it. Thanks. I was addressing the misunderstanding regarding Marco's book and the code in the linked question. Still not sure how SO rep alone qualifies someone as *authoritative*, but you seem to feel you fall into that category. Can you cite the source that provides that qualification? I'd like to see about having it granted for some others as well. – Ken White Aug 13 '15 at 12:51
  • @KenWhite I was just wanting to let you know the outcome in case you had not seen it. It was joking about *authoritative*. Obviously that's not for me to say! I don't set much store by SO rep. I tend to judge people's knowledge here on what they write. – David Heffernan Aug 13 '15 at 12:54
  • @David: I appreciate it. I said thanks. :-) I had previously upvoted your answer. – Ken White Aug 13 '15 at 12:57
  • @Ken Thanks. I guess that what Martyn meant was common use of that word. For instance that given in the Oxford English dictionary: *Able to be trusted as being accurate or true; reliable.* I don't think certificates prove anything, any more than rep proves much. I presume that all Martyn meant was that he regarded what I write here as generally reliable. He used a concise language construct to say in two words, "authoritative source", what could have been written as, "somebody whose answers tend to be reliable". TBC – David Heffernan Aug 14 '15 at 06:40
  • I know that's how I read other peoples' answers. If they have a good track record of writing accurate and informed answers, I'm more inclined to trust them. That's all really. – David Heffernan Aug 14 '15 at 06:41

2 Answers2

6

The solution outlined below works for versions up to and including Delphi Seattle.

For reasons unknown to me, you need to qualify private instance members with Self. So, this compiles:

function TMyHelper.Sum: Integer;
begin
  Result := 0;
  Result := Result + FPublicInt;
  Result := Result + FProtectedInt;
  Result := Result + Self.FPrivateInt;
end;

Contrary to the suggestions in the comments, the same is true for methods. You would need to explicitly include Self. to call a private method in the helpee.

In Delphi 10.1 Berlin and beyond it is no longer possible to access strict private or private members of the helpee in a helper.

LU RD
  • 34,438
  • 5
  • 88
  • 296
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Indeed, it does (compile). I was puzzled by why MC should have chosen an example which, as written, would only work if Helper and Helped were in the same unit. I assumed it was just for compactness of illustration. – MartynA Aug 12 '15 at 19:00
  • Presumably Marco just made a mistake. The documentation is a tad weak isn't it. – David Heffernan Aug 12 '15 at 19:01
  • It was just meant to be the simplest possible demo, but I agree it is misleading. For future editions, I'm replacing private with protected. That code, anyway, worked and still works today in 10.1 Berlin, as access to private in same units is always granted, class helper or not. – Marco Cantù Jun 08 '16 at 13:02
  • Strict protected members are still accessible from a helper in Delphi 10.1 Berlin. I removed that from your answer. – LU RD Jun 16 '16 at 21:04
  • @LURD Thanks. Johan wrote that part! – David Heffernan Jun 16 '16 at 21:06
  • I think @David or Johan is right, as a helper class I had for a TCustomLabel can no longer access the Private DoDrawNormalText in Delphi 10.2 Tokyo, and instead now throws a E2361 compile error. The helper method did originally have: Self.DoDrawNormalText(DC, Text, TextRect, TextFlags); – SiBrit Nov 21 '18 at 03:01
1

To users that use Delphi 10.2/10.3 - i found a article here: How to access a private field from a class helper in Delphi 10.1 Berlin?

Where it stated that using with Self do lets you access private variables from a class helper! I had some helper classes that used self.variable and those gave an error that im not allowed to access private area.

The with Self do fixed this for me! :) so if you run into these problems.. try it yourself..

Ernst Reidinga
  • 203
  • 2
  • 13
  • Just a cautionary warning: This is obviously an overlooked way to "hack" the visibility, and like the "Self." method will probably be closed off in a future Delphi version. Private members are supposed to be just that - private... – HeartWare Jun 17 '19 at 06:42
  • I Agree - but i use a class helper in the TZipFile, where i need to access a private variable to be able to delete a file in a zipfile. Also you where able to edit these before D10.1 , so i dont fully understand why they changed this. – Ernst Reidinga Jun 18 '19 at 15:12
  • They changed this, because it's not supposed to be possible. Private members are supposed to be private. If the compiler doesn't enforce this, it's a bug that needs to be fixed. It's as simple as that... – HeartWare Jun 19 '19 at 14:00
  • What would be the best alternative then? Inherit the ZipFile class and create a ZipFileEx class instead of a class helper? – Ernst Reidinga Jun 19 '19 at 14:23
  • 1
    You have four options: 1) Rely on a compiler bug and do as you do now knowing that it is simply a matter of time before the bug is fixed, 2) Hope the author fixes/updates the class to allow you to do what you want in a proper manner, 3) Find another library that allows you to do what you need, 4) Make a copy of the class in a new source file and make the changes yourself. But note that a descendant class won't help you in this case, as private members are just that - private. You'd need them to be protected in order to access them in descendant classes. – HeartWare Jun 20 '19 at 05:55
  • There are a few more than 4 options available. One option is to use RTTI: https://www.ideasawakened.com/post/how-to-access-private-fields-with-rtti-to-give-trestclient-an-onreceivedata-event – Darian Miller Dec 18 '21 at 21:20