【教程】delphi 中如何访问另一个类中到私有成员方法 N 合 1

此文是参考  原文链接 和群友及作者自己曾经用过的方法简单总结,感谢群内的朋友和原文作者。另外,此文中所谓的访问另一个类的私有成员,另一个类并不在同一单元,在同一单元内的私有成员 Delphi 也是可以直接访问的,不需要绕圈子。

1、最易用的方法:class helper + with self

创建一个对应类的 class helper,然后在其中用 with self do 来访问其私有成员。不过这个不知道是不是算 bug,如果算的话,官方可能会在以后修正,如果不算,大家就这么用吧。

type
   TMyHelperForClass=class helper for TMyClass
   ...
   end;

...TMyHelperForClass.xxxx...
begin
...
with Self do
   begin
   //在这里就可以访问 TMyClass 的私有成员了
   end
end;

2、使用 TMethod 方法访问私有函数

这个本质上来说,就是设置 TMethod.Code 和 Data 成员的地址,其中 Code 对应私有函数地址,然后 Data 指向 Self,然后声明下私有方法的类型,转换下就好。

type
   TPrivateMethodType=... of object;
...
var
   AMethod:TMethod;
   AInvoke:TPrivateMethodType absolute AMethod;
begin
AMethod.Code:=@TMyClass.PrivateMethodName;
AMethod.Data:=Self;
AInvoke(...);
end;

当然,这个仅限于私有函数,不包含成员。

3、最保守的用法:RTTI

利用新版 Delphi 的 Rtti 支持,可以获取私有成员的地址,并进行调用。效率不如前面几种方法,但相对来说,更可控一些。直接搬过来 qdac_fmx_vkhelper 的代码,给大家参考:

procedure UpdateAndroidKeyboardServiceState;
  var
    ASvc: IFMXVirtualKeyboardService;
    AContext: TRttiContext;
    AType: TRttiType;
    AField: TRttiField;
    AInst: TVirtualKeyboardAndroid;
  begin
    if not Assigned(FVKState) then
    begin
      if (not Assigned(Screen.FocusControl)) and
        TPlatformServices.Current.SupportsPlatformService
        (IFMXVirtualKeyboardService, ASvc) then
      begin
        AInst := ASvc as TVirtualKeyboardAndroid;
        AContext := TRttiContext.Create;
        AType := AContext.GetType(TVirtualKeyboardAndroid);
        AField := AType.GetField('FState');
        if AField.GetValue(AInst).AsOrdinal <> 0 then
        begin
          FVKState := PByte(AInst);
          Inc(FVKState, AField.Offset);
        end;
      end;
    end;
    if Assigned(FVKState) and (FVKState^ <> 0) then
      FVKState^ := 0;
  end;

4、有风险的方法:套娃

套娃这种办法,在以前用过,因为要求成员的布局和原始的函数一样,所以我觉得万一原始类升级,这块都得跟着改,所以不太推荐这么用。这种一般是用来访问私有成员变量时比较方便,如果是私有函数,则由于代码地址不一样,不能直接用,但可以通过计算成员函数地址偏移的方式,来完成调用。总体来说,套娃虽好,慎用为佳。

5、最难用的方法:汇编

好吧,这觉得这个已经超过大部分普通程序员的能力了,asm JMP 指令跳转到目标地址去执行就可以调用函数,实际上直接asm call 应该也是一样的。如果函数没有参数还好,有的话,你得研究下如何入栈/出栈/栈平平衡的问题,对于大部分还是算了吧。原文有例子,不过我觉得还是“就这样算了吧,就这样忘子吧,该放就放,再想也没有用,傻傻等待……”,有唱出来的说明是同龄人,老了~~~~

【其它】

访问保护成员的办法就简单多了,直接用class helper 或者是实现一个子类(这个子类啥也不需要做,只是父类的一个转换器),然后就可以了。

分享到: