Delphi:写入后裔类的私人祖先字段
我需要修复第三方组件。这个组件的类有积极使用其子孙私有变量:Delphi:写入后裔类的私人祖先字段
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再创建一个新的类,但我需要用我的修补程序。而且我真的不想在我的代码中再现基类的所有树。
你可以建议一些快速入侵访问该私人领域而不改变原始组件的单位如果可能的话?
您必须使用hack才能访问其他单元中的任何类(包括基类)中的私有字段。在你的情况定义您的单位:
type
__TThirdPartyComponentBase = class
private
FSomeVar: Integer;
end;
然后得到访问:
__TThirdPartyComponentBase(Self).FSomeVar := 123;
当然,这是危险的,因为你需要控制在基类的变化。因为如果字段布局将会改变并且你会错过这个事实,那么上述方法将导致失败,AV等等。
通过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的代码或更改原始组件的单元。 – Andrew 2010-10-28 14:08:20
不知道这是否有用,但我似乎记得有一种方法将私有变量“破解”为可见性。
例如,我知道,当我将属性从较低可见性(在基类中)移动到更可见的级别(在我的后代)时,我遇到了编译器的警告。警告声明它在不同的可见度水平被宣布...
已经有一段时间了,我不确定,但我相信你可以做的是在你的后代中声明与保护相同的变量。 (您可能需要使用Redeclare关键字进行编译。)
对不起,我没有关于如何做到这一点的更多具体信息(如果确实有可能)。也许这篇文章会提示这里的一个向导改正我! :-)
通过使用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.
正如从代码中的注释中可以看出,FSomeVar
由来自TThirdPartyComponentBase
类一类的辅助曝光。 TThirdPartyComponent
的另一个班级帮手实现了Foo过程。在那里,基类助手的SomeVar
属性的访问是通过类型强制转换为基类来实现的。
非常好!我喜欢。 – 2014-09-18 14:13:07
请注意,从Delphi 10.1柏林开始,其他单位的班级助手不能再访问私人成员,因为这被认为是Embarcadero的一个bug。有关更多详细信息,请参阅http://stackoverflow.com/questions/9410485/how-do-i-use-class-helpers-to-access-strict-private-members-of-a-class#comment61026976_9410717。 – 2016-04-21 05:14:43
@Andrew:请注意,只要祖先(第三方)组件的内存布局发生变化,此解决方案就会中断。你可能没有注意到它会中断,因为没有任何东西会提醒你。或者你可能会看到虚假的错误行为(如果你幸运的话:访问违规),因为你开始覆盖不属于你的数据。 – 2010-10-28 15:50:07
@Jeroen Pluimers我已经注意到Andrew关于这个事实。但是这个问题没有其他解决方案。 – oodesigner 2010-10-28 16:14:04
类助手可以做到这一点,没有黑客攻击,看到我的答案:) – 2012-07-07 22:19:47