Accessing private fields from a Class Helper
Asked Answered
M

2

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.

Morez answered 12/8, 2015 at 17:55 Comment(14)
You could access private methods, not fields.Ovary
@TLama: Marco Cantu's example definitely uses fields. I have it in front of me.Morez
Then they must have fix this feature as yet in Delphi 2009 you cannot access fields that way.Ovary
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.Fresher
@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.Morez
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.Fresher
I think the version with "authoritative source" is better! ;-)Cardon
@DavidHeffernan: Arf. I changed it to spare blushes, of course.Morez
@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.Cardon
@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.Fresher
@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.Cardon
@David: I appreciate it. I said thanks. :-) I had previously upvoted your answer.Fresher
@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". TBCCardon
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.Cardon
C
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.

Cardon answered 12/8, 2015 at 18:54 Comment(6)
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.Morez
Presumably Marco just made a mistake. The documentation is a tad weak isn't it.Cardon
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.Phytohormone
Strict protected members are still accessible from a helper in Delphi 10.1 Berlin. I removed that from your answer.Treva
@LURD Thanks. Johan wrote that part!Cardon
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);Exceed
J
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..

Jacksonjacksonville answered 17/6, 2019 at 1:49 Comment(6)
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...Yuonneyup
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.Jacksonjacksonville
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...Yuonneyup
What would be the best alternative then? Inherit the ZipFile class and create a ZipFileEx class instead of a class helper?Jacksonjacksonville
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.Yuonneyup
There are a few more than 4 options available. One option is to use RTTI: ideasawakened.com/post/…Earvin

© 2022 - 2024 — McMap. All rights reserved.