views:

102

answers:

3

I need to fix a third-party component. This component's class has private variable which is actively used by its descendants:

TThirdPartyComponentBase = class
private
  FSomeVar: Integer;
public
  ...
end;

TThirdPartyComponent = class (TThirdPartyComponentBase)
protected
   procedure Foo; virtual;
end;

procedure TThirdPartyComponent.Foo;
begin
  FSomeVar := 1; // ACCESSING PRIVATE FIELD!
end; 

This works because both classes are in the same unit, so they're kinda "friends".

But if I'll try to create a new class in a new unit

TMyFixedComponent = class (TThirdPartyComponent)
  procedure Foo; override; 
end;

I can't access FSomeVar anymore, but I need to use it for my fix. And I really don't want to reproduce in my code all that tree of base classes.

Can you advise some quick hack to access that private field without changing the original component's unit if it's possible at all?

+1  A: 

Expose the value of the private variable by a protected property in TThirdPartyComponent.

TThirdPartyComponent = class (TThirdPartyComponentBase)
private
   Procedure SetValue(Value: Integer);
   Function GetValue: Integer;
protected
   Property MyVar: Integer read GetValue write Setvalue; 
   procedure Foo; virtual;
end;

Procedure TThirdPartyComponent.SetValue(Value: Integer);
begin
  FSomeVar := Value ;
end;

Function GetValue: Integer;
begin
  result := FSomeVar;
end;

In TMyFixedComponent class use the MyVar Property in the procedure which you would like to override.

Bharat
But this will change the original code of TThirdPartyComponent. I wanted some solution without rewriting TThirdPartyComponent's code or changing the original component's unit.
Andrew
+3  A: 

You have to use a hack to access a private field in any class (including a base class) in a different unit. In your case define in your unit:

type
  __TThirdPartyComponentBase = class 
  private 
    FSomeVar: Integer;
  end;

Then get the access:

__TThirdPartyComponentBase(Self).FSomeVar := 123;

Of course, that is dangerous, because you will need to control changes in the base class. Because if the fields layout will be changed and you will miss this fact, then the above approach will lead to failures, AV's, etc.

oodesigner
Thanks! It works
Andrew
@Andrew: note that this solution will break as soon as the memory layout of the ancestor (3rd party) component changes. You might not notice it breaks, as nothing will warn you about it. Or you might see spurious wrong behaviour (if you are lucky: access violations) because you start to overwrite data that is not yours.
Jeroen Pluimers
@Jeroen Pluimers I have notices Andrew about that fact. But there is no other solutions for this issue.
oodesigner
A: 

Don't know if this will help, but I seem to recall there is a way to "crack" a private variable into visibility.

I know, for example, I've encountered warnings from the compiler when I've moved a property from lower visibility (in the base class) to a more visible level (in my descendant). The warning stated that it's being declared at a different level of visibility...

It's been some time and I'm not certain, but I believe what you can do is in your descendant declare the same variable as protected. (You may have to use the Redeclare keyword for this to compile.)

Sorry I don't have more specific information on how to do this (if it's indeed possible.) Perhaps this posting will prompt one of the wizards here into correcting me! :-)

Robert Frank