Jump to content
pcplayer99

How to operate a private field in other unit?

Recommended Posts

In unit Soap.SOAPLinked.pas there are:

 

1. TLinkedWebNode

2. TLogLinkedWebNode = class(TLinkedWebNode)

3. TLinkedRIO = class(TRIO)

 

in TLinkedRIO, it has two constructor: 

one is for: FLinkedWebNode := TLinkedWebNode.Create(nil); 

and another is for: FLinkedWebNode := TLogLinkedWebNode.Create(nil);

 

My question is:

if I want to create a new TLinkedWebNode like TLogLinkedWebNode, Just named it TMyWebNode, I must let TLinkedRIO has a new constructor that lets it create FLinkedWebNode  as my new TMyWebNode.

 

but, I can not modify Soap.SOAPLinked.pas because it is source code in Delphi. I will create a new pas file and inherit TLinkedRIO like TMyRIO = class(TLinkedRIO) and add a new constructor in it.

 

but, the new TMyRIO in a new pas unit, I can not access FLinkedWebNode like FLinkedWebNode := TMyWebNode.Create(nil); 

 

So, is there any way to do that? or this is a design issue in Soap.SOAPLinked.pas and I must modify it?

 

 

Share this post


Link to post

Not sure I correctly understand what need and what you've done. I understood that you copied Delphi source code in another unit and then modified that unit to fit you needs. Then why not modify it further to make visible what you need ?

 

btw: Modifying a Delphi unit although technically correct, will make your application difficult to maintain on the long term as Delphi source code will change: you'll have to apply your changes, if possible, to the new source and this may be difficult or even impossible. To avoid this problem, you probably have to change your design.

 

 

Share this post


Link to post

You cannot access private fields of a class declared in a different unit. [Period]

 

That is, if you play by the rules. There are various ways to still achieve that, but each of them relies on either some compiler quirk (e.g. class helpers could access private fields in some Delphi versions, but that loophole has been closed) or directly use the knowledge about the memory layout of the original class. The latter is done either with pointer arithmetic or by declaring a new class with the same fields and hard typecasting an instance to that class, which then allows accessing the private fields. Of course that would mean this class or pointer arithmetic must be adapted to every change that Embarcadero makes to the original, so the memory layout matches.

Edited by dummzeuch
  • Like 1

Share this post


Link to post
11 hours ago, pcplayer99 said:

So, is there any way to do that?

How to call a private method of a class inside a Berlin+ class via Helper

procedure TSomeClassHelper.CheckAccessToPrivate;
begin
 With Self do begin // access via with works
   FInt :=1;
   SomeMethod;
 end;
end;

// Declared in another unit as:
type TSomeClass = class
    private
      FInt : integer;
      procedure SomeMethod;
  end;

 

  • Like 1
  • Thanks 1

Share this post


Link to post
20 hours ago, FPiette said:

Not sure I correctly understand what need and what you've done. I understood that you copied Delphi source code in another unit and then modified that unit to fit you needs. Then why not modify it further to make visible what you need ?

 

btw: Modifying a Delphi unit although technically correct, will make your application difficult to maintain on the long term as Delphi source code will change: you'll have to apply your changes, if possible, to the new source and this may be difficult or even impossible. To avoid this problem, you probably have to change your design.

 

 

 

Yes, this is why I do not want to copy it and modify it, I just want to inherit a new class in a new unit, to avoid modify Delphi's source file.

Share this post


Link to post
10 hours ago, FredS said:

How to call a private method of a class inside a Berlin+ class via Helper


procedure TSomeClassHelper.CheckAccessToPrivate;
begin
 With Self do begin // access via with works
   FInt :=1;
   SomeMethod;
 end;
end;

// Declared in another unit as:
type TSomeClass = class
    private
      FInt : integer;
      procedure SomeMethod;
  end;

 

 

Thank you Fred. I will try helper class mode.

Share this post


Link to post

I have done a test if the class helper is in a different unit with the original class, it can not access  private field in original class.

Share this post


Link to post
7 hours ago, pcplayer99 said:

I have done a test if the class helper is in a different unit with the original class, it can not access  private field in original class.

As I wrote above:

On 9/26/2020 at 9:46 AM, dummzeuch said:

There are various ways to still achieve that, but each of them relies on either some compiler quirk (e.g. class helpers could access private fields in some Delphi versions, but that loophole has been closed)

So, depending on your Delphi version this may still work, but it won't in the later version(s).

Share this post


Link to post
Guest
19 hours ago, FredS said:

How to call a private method of a class inside a Berlin+ class via Helper

Then "berlin+" is not correct. I almost lost my chin here... (?)

 

I mean, if you must edit the unit then you could edit that unit, and instead move the member to the protected section.

It will give a compile error when the unit is updated rather than creating havoc that could slip inte a production build.

Share this post


Link to post
12 hours ago, pcplayer99 said:

it can not access  private field in original clas

 

I have this test setup to let me know if this is ever changed in a new version of Delphi.

Should be simple enough to replace TCustomTextFile with whatnot and run the test which I just ran in 10.4.1 and it passed:

 

//MMWIN:CLASSCOPY
unit _MM_Copy_Buffer_;

interface


implementation

type
  TTestAccessToPrivateFieldHelper = class helper for TCustomTextFile
    function SetPrivateOwnsStream(AValue: boolean): boolean;
end;


{ TTestAccessToPrivateFieldHelper }

function TTestAccessToPrivateFieldHelper.SetPrivateOwnsStream(AValue: boolean): boolean;
begin
  with Self do begin
    FOwnsStream := AValue;
    Result := FOwnsStream;
  end;
end;

end.
//MMWIN:MEMBERSCOPY
unit _MM_Copy_Buffer_;

interface

type
  THelperTests = class(TObject)
  public
    [Test(True)]
    procedure TestAccessToPrivateField;
  end;


implementation

procedure THelperTests.TestAccessToPrivateField;
var F : TTextFile;
begin
  F := TTextFile.Create('testing.txt', TEncoding.UTF8);
  try
    Assert.IsFalse(F.SetPrivateOwnsStream(False));
    Assert.IsTrue(F.SetPrivateOwnsStream(True));
  finally
    F.Free;
  end;
end;


end.

 

  • Like 1

Share this post


Link to post
4 hours ago, Dany Marmur said:

Then "berlin+" is not correct.

 

Works here, must be Magic 🙂

Share this post


Link to post
22 minutes ago, FredS said:

 

Works here, must be Magic 🙂

 

My test code as:

 

constructor TMyClassB3.Create(B: Boolean);
begin
  if B then FMyClassA := TMyClassA2.Create;  
end;

 

The field "FMyClassA" is declared in TMyClassB2 in another unit, and this code can not compile. because field "FMyClassA" is a private field of TMyClassB2.

Share this post


Link to post
28 minutes ago, pcplayer99 said:

The field "FMyClassA" is declared

 

You have to use the Helper, only it and the use of WITH get you access, see my test code above.

 

Plus that's not enough code for anyone to help you..

 

// Your helper should have a method like SetPrivateVar which uses 'With Self do'

 SomeClass.SetPrivateVar(TMyClassA2.Create());
 
 

 

  • Like 2

Share this post


Link to post

Thanks FredS, 

after I add "With Self do"  in my helper class, it can access provate field in another unit.

 

 

in unit1, here is TMyClassB:

 

TMyClassB = class
  private
    FMyClassA: TMyClassA;
    function GetMyName: string;
  public
    constructor Create;

    property MyName: string read GetMyName;
  end;

 

 

in Unit2, here is my helper class:

TMyClassB3 = class helper for TMyClassB
  public
    constructor Create(B: Boolean); overload;
  end;

 

constructor TMyClassB3.Create(B: Boolean);
begin
  //if B then FMyClassA := TMyClassA2.Create;  //helper class can not access private field.

 

  //use with self do, here can access private field
  with Self do
  begin
    FMyClassA := TMyClassA2.Create;
  end;
end;

 

 

Share this post


Link to post

This is the last (known) remaining class helper loop hole that was overlooked when they closed the previous one that didn't require a 'with' statement. Don't expect this to keep working in future Delphi versions.

 

(OK, I'll shut up with this warning now.)

Share this post


Link to post
6 minutes ago, dummzeuch said:

Don't expect this to keep working in future Delphi versions.

I'm still hoping that with will be completely eliminated from the language.

Here's a few lines from the code I'm currently working on:

begin
  ...3-400 lines of code...
  with vPlan.DataController, dbSlaktePlan, BLPlan, Grupper ,Data[Id] do
  begin
    ...a few hundre lines more...
    with vPlan, BLPlan.SiteSum, Plans[Id] do
    begin
      ...and it goes on...

 

  • Like 1
  • Sad 1

Share this post


Link to post
6 minutes ago, Anders Melander said:

I'm still hoping that with will be completely eliminated from the language.

Here's a few lines from the code I'm currently working on:


begin
  ...3-400 lines of code...
  with vPlan.DataController, dbSlaktePlan, BLPlan, Grupper ,Data[Id] do
  begin
    ...a few hundre lines more...
    with vPlan, BLPlan.SiteSum, Plans[Id] do
    begin
      ...and it goes on...

 

Not likely, because there is lots of code like this which would break, keeping people from updating which would hurt the bottom line.

The best we can hope for is a warning or maybe even a switch to turn 'with' off. And, actually, seeing how many 'with' statements there are even in the RTL / VCL (haven't checked FMX) and even in our own code (where I try to eliminate every 'with' I come across when I edit code), that's probably a good thing.

 

But we digress...

Edited by dummzeuch

Share this post


Link to post

with MyDataSet do

begin

  FieldByName('aaa').AsString := 'abcd';

  FieldByName('bbb').AsInteger := 234;

  etc...

end;

 

This ca reduce typing.

Share this post


Link to post
1 hour ago, Anders Melander said:

I'm still hoping that with will be completely eliminated from the language.

Here's a few lines from the code I'm currently working on:

F.ex., C-like langs are full of such constructions that allow such write-only clauses as "while (*p++=*r++)" or "i+++++j" but noone tries to throw them out. Shittycode could be written in any language even in Brainfuck (lol)

Edited by Fr0sT.Brutal

Share this post


Link to post
Guest
1 hour ago, dummzeuch said:

But we digress...

Shurely, we do and let me go on! A compiler flag (with: off) and good refactoring support is on my wish-list!

I understand that since the debugger cannot figure out the scope then refactoring would maybe be tricky to implement 😞

IMHO "hate" is a very strong word that i seldom use, if at all. I hate with.

Share this post


Link to post
1 hour ago, pcplayer99 said:

This ca reduce typing.

I dnt knw bout u, bt I gt $ 4 the Q of my cde. Not for the amount of keystrokes I can save.

  • Haha 1

Share this post


Link to post
1 hour ago, Stefan Glienke said:

Hundreds of lines of code within one routine and you think that *with* is the problem? LOL

Yes, because with this WITHs it's very hard to do secure refactorings to solve other problems.

  • Like 1

Share this post


Link to post
1 hour ago, Stefan Glienke said:

Hundreds of lines of code within one routine and you think that *with* is the problem? LOL

That almost sounded like an insult but assuming that wasn't the intention; I didn't write the code but I've been tasked with maintaining, fixing and modernizing it.

with isn't the only problem but it's the one that makes refactoring the code a much harder task than it should have been - and it makes the code undebuggable. Among the other beauties in this code base are the local procedures; The above was actually found in a local procedure, several levels down. The top level method is a few thousand lines of pure horror.

  • Sad 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×