Delphi:在派生类中写入私有祖先字段

12

我需要修复一个第三方组件。该组件的类有一个私有变量,这个变量被它的子类所活跃地使用:

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; 

这样做是因为这两个类在同一个单元中,所以它们有点像“朋友”。

但如果我尝试在一个新的单元中创建一个新类

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

我不能再访问FSomeVar,但我需要在我的修复程序中使用它。我真的不想在我的代码中重新编写所有基类的树。

如果可能的话,您能否建议一些快速的方法来访问该私有字段,而不更改原始组件单元

4个回答

20
通过使用“类帮助器”,可以在派生类中访问基类的私有成员,而不会失去类型安全性。只需在另一个单元中添加这些声明即可:
```html

通过使用`类帮助器`,可以在派生类中访问基类的私有成员,而不会失去类型安全性。

只需在另一个单元中添加以下声明:

```
Uses YourThirdPartyComponent;

type
  // A helper to the base class to expose FSomeVar
  TMyBaseHelper = class helper for TThirdPartyComponentBase
  private
    procedure SetSomeVar( value : integer);
    function GetSomeVar: integer;
  public
    property SomeVar:integer read GetSomeVar write SetSomeVar;
  end;

  TMyFixedComponent = class helper for TThirdPartyComponent
  protected
    procedure Foo;
  end;

procedure TMyFixedComponent.Foo;
begin
  // Cast to base class and by the class helper TMyBaseHelper the access is resolved
  TThirdPartyComponentBase(Self).SomeVar := 1; 
end;

function TMyBaseHelper.GetSomeVar: integer;
begin
  Result := Self.FSomeVar; // ACCESSING PRIVATE FIELD!
end;

procedure TMyBaseHelper.SetSomeVar(value: integer);
begin
  Self.FSomeVar := value; // ACCESSING PRIVATE FIELD!
end;

// Testing
var
  TSV: TThirdPartyComponent;
begin
  TSV := TThirdPartyComponent.Create;
  try
    TSV.Foo;    
    WriteLn(IntToStr(TSV.SomeVar));  // Writes 1
  finally
    TSV.Free;
  end;
end.

从代码的注释中可以看出,FSomeVar是由TThirdPartyComponentBase类的一个类帮助器公开的。 另一个TThirdPartyComponent的类帮助器实现了Foo过程。 在这里,通过将类型转换为基类,访问基类帮助器的SomeVar属性。


1
非常好!我喜欢它。 - J...
5
请注意,自 Delphi 10.1 Berlin 起,在另一个单元中的类助手不再能访问私有成员,因为 Embarcadero 将其视为错误。详见 https://dev59.com/JWox5IYBdhLWcg3wIQ_3#tK-hEYcBWogLw_1bLhIH 获取更多细节。 - Marc Durdin

6

如果要访问不同单元中的任何类(包括基类)中的私有字段,您需要使用一种技巧。在您的情况下,请在您的单元中定义:

type
  __TThirdPartyComponentBase = class 
  private 
    FSomeVar: Integer;
  end;

然后获取访问权限:
__TThirdPartyComponentBase(Self).FSomeVar := 123;

当然,这是很危险的,因为您需要控制基类中的更改。如果字段布局被更改并且您错过了这个事实,那么上述方法将导致失败、AV等问题。


2
@Andrew:请注意,只要祖先(第三方)组件的内存布局发生更改,此解决方案就会崩溃。您可能不会注意到它的崩溃,因为没有任何警告。或者,您可能会看到虚假的错误行为(如果您很幸运:访问违规),因为您开始覆盖不属于您的数据。 - Jeroen Wiert Pluimers
@Jeroen Pluimers 我已经向 Andrew 汇报了这个事实。但是对于这个问题没有其他解决方案。 - oodesigner
类助手可以在不进行黑客攻击的情况下完成此操作,详见我的回答 :) - LU RD
@LURD 类助手仅适用于 Delphi 2007 或 2009。(我不确定。) - mg30rg
@mg30rg,类助手是在.NET(D8)和D2005中引入的。 - LU RD

0

不知道这是否有帮助,但我似乎记得有一种方法可以将私有变量“破解”为可见性。

例如,我曾遇到过编译器警告,当我将属性从较低的可见级别(在基类中)移动到更高的级别(在我的子类中)时。警告指出它被声明在不同的可见级别上...

虽然已经过了一些时间,我也不确定,但我相信您可以在您的子类中声明与父类中相同的变量,并将其设置为受保护的。(这可能需要使用“Redeclare”关键字才能编译。)

很抱歉我没有更具体的信息告诉您如何做到这一点(如果确实可能)。也许这篇文章会激发这里的某一位专家来纠正我! :-)


-1
在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;

TMyFixedComponent 类中,您可以在要重写的过程中使用 MyVar 属性。

5
但这会改变TThirdPartyComponent的原始代码。我希望有一种解决方案,不需要重写TThirdPartyComponent的代码或更改原始组件的单元。 - Andrew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接