我想使用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;
如您所见,它需要一些额外的输入,但与修补整个单元相比,它非常简单。