如何从Delphi 10.1 Berlin中的类助手访问私有字段?

sta*_*mik 10 delphi class-helpers delphi-10.1-berlin

我想使用Gabriel Corneanu的jpegex,jpeg.TJPEGImage的类助手.阅读这个这个,我了解到,超过德尔福西雅图你不能访问私有字段不再像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;
Run Code Online (Sandbox Code Playgroud)

Too*_*the 22

今天我使用with语句找到了解决这个bug的巧妙方法.

function TValueHelper.GetAsInteger: Integer;
begin
  with Self do begin
    Result := FData.FAsSLong;
  end;
end;
Run Code Online (Sandbox Code Playgroud)

此外,Embarcadero做了很好的工作来建造墙壁来保护私人部分,这也许就是为什么他们将它命名为10.1柏林.

  • 人类已知的唯一建设性用途`with`! (8认同)

Uwe*_*abe 14

谨防!这是一个讨厌的黑客,并且当被黑客类的内部字段结构发生变化时可能会失败.

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;
Run Code Online (Sandbox Code Playgroud)

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

有关其工作原理的完整说明,请访问:

Hack#5:访问私有领域


LU *_* RD 7

通过使用类助手和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;
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,它需要一些额外的打字,但与修补整个单元相比,它很简单.