如何从德尔福 10.1 柏林的类助手访问私有字段



我想使用Gabriel Corneanu的jpegex,这是jpeg的类助手。TJPEGImage。阅读本文和本文,我了解到,在Delphi Seattle之外,您不能再像jpegex那样访问私有字段(在下面的示例中为FData)。像David Heffernan提议的那样使用VMT远远超出了我的范围。有没有更简单的方法来完成这项工作?

   type
  // helper to access TJPEGData fields
  TJPEGDataHelper = class helper for TJPEGData
    function  Data: TCustomMemoryStream; inline;
    procedure SetData(D: TCustomMemoryStream);
    procedure SetSize(W,H: integer);
  end;
// TJPEGDataHelper
function TJPEGDataHelper.Data: TCustomMemoryStream;
begin
  Result := self.FData;
end;

今天,我使用 with 语句找到了解决此错误的巧妙方法。

function TValueHelper.GetAsInteger: Integer;
begin
  with Self do begin
    Result := FData.FAsSLong;
  end;
end;

除此之外,Embarcadero在建造墙壁以保护私人部位方面做得很好,这可能就是他们将其命名为10.1柏林的原因。

当心!这是一个令人讨厌的黑客攻击,当被黑客攻击类的内部字段结构发生变化时,可能会失败。

type
  TJPEGDataHack = class(TSharedImage)
    FData: TCustomMemoryStream; // must be at the same relative location as in TJPEGData!
  end;
  // TJPEGDataHelper
function TJPEGDataHelper.Data: TCustomMemoryStream;
begin
  Result := TJPEGDataHack(self).FData;
end;

仅当"hack"类的父类与原始类的父类相同时,这才有效。因此,在这种情况下,TJPEGData 继承自 TSharedImage,"hack"类也是如此。这些位置也需要匹配,因此如果列表中在FData之前有一个字段,那么即使没有使用它,也应该在"hack"类中放置一个等效字段。

可以在此处找到其工作原理的完整说明:

黑客#5:访问私有字段

通过使用类帮助程序和RTTI的组合,可以使用类帮助程序获得与以前的Delphi版本相同的性能。

诀窍是使用 RTTI 在启动时解析私有字段的偏移量,并将其作为类 var 存储在帮助程序中。

type 
  TBase = class(TObject)
  private  // Or strict private
    FMemberVar: integer;
  end;
type
  TBaseHelper = class helper for TBase // Can be declared in a different unit
  private
    class var MemberVarOffset: Integer;
    function GetMemberVar: Integer;
    procedure SetMemberVar(value: Integer);
  public
    class constructor Create;  // Executed automatically at program start
    property MemberVar : Integer read GetMemberVar write SetMemberVar;
  end;
class constructor TBaseHelper.Create;
var
  ctx: TRTTIContext;
begin
  MemberVarOffset := ctx.GetType(TBase).GetField('FMemberVar').Offset;
end;
function TBaseHelper.GetMemberVar: Integer;
begin
  Result := PInteger(Pointer(NativeInt(Self) + MemberVarOffset))^;
end;
procedure TBaseHelper.SetMemberVar(value: Integer);
begin
  PInteger(Pointer(NativeInt(Self) + MemberVarOffset))^ := value;
end;

如您所见,它需要一些额外的输入,但与修补整个单元相比,它非常简单。

最新更新