11

我想使用 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;
4

3 回答 3

26

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

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

除此之外,Embarcadero 还出色地建造了围墙来保护私密部位,这可能就是他们将其命名为 10.1 Berlin 的原因。

于 2017-03-21T19:47:00.773 回答
14

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

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:访问私有字段

于 2016-05-20T16:36:34.490 回答
9

通过使用类助手和 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;

如您所见,它需要一些额外的输入,但与修补整个单元相比,它已经足够简单了。

于 2016-06-16T14:26:23.940 回答