Delphi:写入后代类中的私有祖先字段

Posted

技术标签:

【中文标题】Delphi:写入后代类中的私有祖先字段【英文标题】:Delphi: writing to the private ancestor's field in descendant class 【发布时间】:2011-05-01 21:44:23 【问题描述】:

我需要修复第三方组件。这个组件的类有私有变量,被它的后代积极使用:

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,但我需要使用它来进行修复。而且我真的不想在我的代码中重现所有基类树。

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

【问题讨论】:

【参考方案1】:

通过使用class helpers,可以实现从派生类对基类私有部分的访问,而不会失去类型安全性。

只需在另一个单元中添加这些声明:

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.

从代码中的 cmets 可以看出,FSomeVar 由来自 TThirdPartyComponentBase 类的类助手公开。 TThirdPartyComponent 的另一个类助手实现了 Foo 过程。在那里,对基类助手的SomeVar 属性的访问是通过类型转换为基类进行的。

【讨论】:

非常好!我喜欢它。 请注意,从 Delphi 10.1 Berlin 开始,另一个单元中的类助手不能再访问私有成员,因为 Embarcadero 认为这是一个错误。更多详情请见***.com/questions/9410485/…【参考方案2】:

您必须使用 hack 来访问不同单元中任何类(包括基类)中的私有字段。在您的情况下,在您的单位中定义:

type
  __TThirdPartyComponentBase = class 
  private 
    FSomeVar: Integer;
  end;

然后获取访问权限:

__TThirdPartyComponentBase(Self).FSomeVar := 123;

当然,这很危险,因为您需要控制基类中的更改。因为如果字段布局发生变化,你会错过这个事实,那么上述方法将导致失败、AV 等。

【讨论】:

@Andrew:请注意,一旦祖先(第 3 方)组件的内存布局发生更改,此解决方案就会中断。你可能不会注意到它坏了,因为没有任何东西会警告你。或者您可能会看到虚假的错误行为(如果幸运的话:访问违规),因为您开始覆盖不属于您的数据。 @Jeroen Pluimers 我注意到 Andrew 关于这一事实。但是这个问题没有其他解决方案。 类助手可以在没有黑客攻击的情况下做到这一点,看我的回答:) @LURD 类助手仅在 Delphi 2007 或 2009 中可用。(我不确定。) @mg30rg,.net (D8) 和 D2005 中引入了类助手。【参考方案3】:

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

我知道,例如,当我将属性从较低的可见性(在基类中)移动到更可见的级别(在我的后代中)时,我遇到了来自编译器的警告。警告表明它正在以不同的可见性级别声明...

已经有一段时间了,我不确定,但我相信你能做的就是在你的后代中声明与受保护的变量相同的变量。 (您可能必须使用 Redeclare 关键字才能编译。)

抱歉,我没有关于如何执行此操作的更具体信息(如果确实可能的话)。也许此帖子会促使这里的一位向导纠正我! :-)

【讨论】:

【参考方案4】:

通过 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 属性。

【讨论】:

但这会改变TThirdPartyComponent的原始代码。我想要一些解决方案,而无需重写 TThirdPartyComponent 的代码或更改原始组件的单位。

以上是关于Delphi:写入后代类中的私有祖先字段的主要内容,如果未能解决你的问题,请参考以下文章

如何防止修改类中的私有字段?

访问类中的私有字段[重复]

用私有属性替换类中的每个字段是一种不好的做法吗? [复制]

在无向图中如何定义祖先和后代?

如何在线性“祖先-后代”关系中找到最后一个后代(与其他条件匹配)

Jquery:获取祖先(或后代)和自我