使用伪类访问私有字段

时间:2018-12-22 22:22:52

标签: delphi

我有这个声明:

Type

  TmyObjA = class
  private
    FieldA: integer;
    FieldB: integer;
  public
    ...
  end;

  TmyObjB = class(TmyObjA)
  private
    FieldC: integer;
    FieldD: integer;
  public
    ...
  end;

  var MyObjB = TmyObjB 

我现在要访问FieldA中的私有字段 MyObjB。我喜欢这样:

  TmyObjAPrivateAccess = class
  public
    FieldA: integer;
    FieldB: integer;
  end;

  TmyObjAPrivateAccess(MyObjB).FieldA := something;

它会起作用还是我必须这样做:

  TmyObjBPrivateAccess = class
  public
    FieldA: integer;
    FieldB: integer;
    FieldC: integer;
    FieldD: integer;
  end;

  TmyObjBPrivateAccess(MyObjB).FieldA := something;

NB:我测试过,它在两种变体中都可以工作,但这不是演示,它似乎可以工作


编辑/注释

关于为什么我需要访问私有成员的原因:我想访问私有成员的类是TTexture

  TTexture = class(TInterfacedPersistent, ITextureAccess)
  private
    FWidth: Integer;
    FHeight: Integer;
    FPixelFormat: TPixelFormat;
    FHandle: TTextureHandle;
    FStyle: TTextureStyles;
    FMagFilter: TTextureFilter;
    FMinFilter: TTextureFilter;
    FTextureScale: Single;
    FRequireInitializeAfterLost: Boolean;
    FBits: Pointer;
    FContextLostId: Integer;
    FContextResetId: Integer;
  protected
  public
    property BytesPerPixel: Integer read GetBytesPerPixel;
    property MinFilter: TTextureFilter read FMinFilter write SetMinFilter;
    property MagFilter: TTextureFilter read FMagFilter write SetMagFilter;
    property PixelFormat: TPixelFormat read FPixelFormat write SetPixelFormat;
    property TextureScale: Single read FTextureScale; // hi resolution mode
    property Style: TTextureStyles read FStyle write SetStyle;
    property Width: Integer read FWidth write SetWidth;
    property Height: Integer read FHeight write SetHeight;
    property Handle: TTextureHandle read FHandle;
  end;

这是我的TmyObjA

然后将此类更新为

TALPlanarTexture = class(TTexture)
  private
    FSecondTexture: TTexture;
    FThirdTexture: TTexture;
    FFormat: TALTextureFormat;
  protected
  ....
  end;

这是我的TmyObjB。但是我还有其他几个像这样的类,例如TALBiPlanarTexture = class(TALTexture)等。这就是为什么我只希望对所有人使用一个 access 类的原因:< / p>

  {************************}
  {$IF CompilerVersion > 32} // tokyo
    {$MESSAGE WARN 'Check if FMX.Types3D.TTexture still has the exact same fields and adjust the IFDEF'}
  {$ENDIF}
  TALTextureAccessPrivate = class(TInterfacedPersistent)
  public
    FWidth: Integer;
    FHeight: Integer;
    FPixelFormat: TPixelFormat;
    FHandle: TTextureHandle;
    FStyle: TTextureStyles;
    FMagFilter: TTextureFilter;
    FMinFilter: TTextureFilter;
    FTextureScale: Single;  
    FRequireInitializeAfterLost: Boolean;
    FBits: Pointer;
    FContextLostId: Integer;
    FContextResetId: Integer;
  protected
  public
  end;

现在为什么我需要访问TTExture的私有成员? TTexture是所有OpenGL delphi框架都使用的非常简单的类。因此,替换此类的意思是……很好的意思是重新绘制了所有的openGL firemonkey框架:)相当复杂的工作,但我同意对于不想使用私有成员的<纯粹>精通人的可能性。

我有另一个框架(视频播放器),在每个视频框架上都给了我一个带有/高度和比例的openGL纹理ID。因此,在每个新的框架事件上,我都需要更新其TTexture的Handle及其With / height并将其发送到canvas.drawTexture(仅接受{{1 }})

是否可以通过给定的TTexture / TTexture / Handlewidth在每个帧上更新height对象?简单回答:不!是的,他们的方式是一种方法,但我发现scale比直接访问私有成员要多得多。这种方式包括使用hazardous将句柄重置为零,然后调用ITextureAccesssetwith将最终确定纹理,然后再设置回切句柄,等等...

但是无论如何,我的问题不是关于访问私人成员是好是坏:)

3 个答案:

答案 0 :(得分:5)

如果您控制所有类,那么这是一个错误的设计选择。但是,您似乎正在尝试访问某些VCL内部组件。如果您确定没有干净的方法来获取所需的信息,那么您所做的是正确的。

您基本上是在定义一个与相同布局作为原始类的新类,然后在不进行类型检查的情况下进行转换:由于您要访问的字段位于以下事实在两个类中具有相同的偏移量,您最终将访问所需的内存。

您的第二个示例有效,因为用户字段正确对齐。如果要访问A的私有字段,最好使用最小填充量的访问器来达到所需的偏移量,这样就可以减少要维护的字段。您应该记录要访问哪些字段以供将来参考,以及原始的类定义,以便可以发现布局随时间的变化。

请记住,当原始类更改其自己的布局时,此肮脏的把戏将不再起作用。根据类的重构方式,您的代码可能只是读取了错误的值,或者因访问冲突而崩溃。

更新 首先,OP要求访问一个普通班级的私有字段。接口未按此答案处理,因为它们按@rudy所述进行了布局,并且显然很难访问这些字段。

答案 1 :(得分:2)

注意。这可能有效,但是我看到TTexture 实现了一个接口。这意味着它将具有指向接口VMT的隐藏实例字段,例如 IPrintable IEDitable IComparable 我的这张图中的字段:

enter image description here

现在,如果在私有成员之前(之前)插入了隐藏的 ITextureAccess 字段(在图中该类的 more字段部分之前),假类的成员(更多字段,但没有隐藏字段)很可能具有错误的偏移量,因此您的把戏可能完全可以。

因此对于这种情况,您至少应测试。如果您这样做了,并且得到了预期的结果,那么您就有一个有效的破解方法。

您对警告消息的使用值得称赞。

更新

我检查了TTexture的偏移量。 ITextureAccess 的接口指针在 FContextResetId 成员字段的之后插入。 之前的所有有效偏移量。


但是 TTexture 具有要设置的属性的 public 属性: Handle Width ,< em> Height ,并且界面 ITextureAccess 允许您设置比例,因此您可能不需要

答案 2 :(得分:0)

只需调用 FieldAddress 即可访问内存中的字段:

var
  LHeight: PInteger;
begin
  LHeight := Self.FieldAddress('FHeight');
  Assert(LHeight <> nil);
  LHeight^ := 100;

注意:你也可以在其他对象中使用这个,这个方法是公开的