我可以用类助手调用静态私有类方法吗?

Fre*_*ing 4 delphi

特别是,我觉得有必要在TCharacter.IsLatin1private.

type
  TCharacterHelper = class helper for TCharacter
  public
    class function IsLatin1(C: Char): Boolean; static; inline;
  end;

class function TCharacterHelper.IsLatin1(C: Char): Boolean;
begin
  Result := Ord(C) <= $FF;
end;
Run Code Online (Sandbox Code Playgroud)

这种单线方法可以在短时间内重新实现,但我最好留下供应商自行决定的确切实现细节.

有没有办法将这种方法"重新引入" public可见性?

LU *_* RD 8

有没有办法将这种方法"重新引入"公众可见度?

是.通过一个新的类函数引入一个非静态函数调用.这里的技巧是使用帮助程序能力来访问所有成员Self.见Access a strict protected property of a Delphi class?How do I use class helpers to access strict private members of a class?.这是通过从新类函数调用私有帮助程序非静态函数来完成的,其中Self可以解析.

Type

  TCharacterHelper = class helper for TCharacter
  private
    class function IsLatin1Cracker(aChar: Char): Boolean; inline;
  public
    // Introduce a new public static class function
    class function IsLatinOne(aChar: Char): Boolean; static; inline;
  end;

class function TCharacterHelper.IsLatinOne(aChar: Char): Boolean;
begin
  Result := IsLatin1Cracker(aChar);
end;

class function TCharacterHelper.IsLatin1Cracker(aChar: Char): Boolean;
begin
  Result := Self.IsLatin1(aChar);  // Here Self can access base class
end;
Run Code Online (Sandbox Code Playgroud)

但是,您不能使用原始方法名称,但仍可以通过这种方式调用原始类函数.


哎呀,大卫展示了一种方法来扩展这个想法以使用原始名称.这可能是工具箱中的一个多功能技巧.


只是想参考哪些文档对此有所说明:

Ordinary Class Methods:

您可以使用Self来调用构造函数和其他类方法,或者访问类属性和类字段.

Class Static Methods:

与普通的类方法不同,类静态方法根本没有Self参数.

注意:与类不同,记录只能有静态类方法.

Class and Record Helpers:

您可以在合法使用扩展类或记录的任何地方使用帮助程序.然后编译器的解析范围变为原始类型,再加上帮助程序.

...

可见范围规则和memberList语法与普通类和记录类型的语法相同.

您可以使用单个类型定义和关联多个帮助程序.但是,源代码中的任何特定位置只应用零或一个助手.最近范围中定义的帮助程序将适用.类或记录助手范围以正常的Delphi方式确定(例如,在单元的uses子句中从右到左).


如上所述,记录只能有静态类方法.因此,如果您想在记录中"重新引入"私有类方法,这里有一个解决方案(基于David的技术):

假设我们有:

Type
  TTestRec = record
  private
    class Function IsLatin1(C: Char): Boolean; static; inline;
  end;
Run Code Online (Sandbox Code Playgroud)

并在新单元中添加一个帮助器:

unit HelperUnitForTTestRec;

interface

Type
  TTestRecHelper = record helper for TTestRec
  public
    class function IsLatin1(c:Char): Boolean; static; //inline; !! Inlining not possible
  end;

implementation

Type
  TTestRecCracker = record helper for TTestRec
  private
    function IsLatinOne(C:Char): Boolean; inline;
  public
    class function IsLatin1Cracker(c:Char): Boolean; static; inline;
  end;

function TTestRecCracker.IsLatinOne(c: Char): Boolean;
begin
  Result := Self.IsLatin1(C);  // <-- Here is Self resolved
end;

class function TTestRecCracker.IsLatin1Cracker(c: Char): Boolean;
var
  tmp: TTestRec;
begin
  Result := tmp.IsLatinOne(C); // <-- Must use a call to ordinary method
end;

class function TTestRecHelper.IsLatin1(c: Char): Boolean;
begin
  Result := IsLatin1Cracker(C);
end;

end.
Run Code Online (Sandbox Code Playgroud)


Dav*_*nan 6

请参阅下面的更新

众所周知,帮助者会破坏私人知名度.因此,私人成员可以从类助手中看到.但是,此行为不会扩展到静态成员,因此TCharacter.IsLatin1在声明它的单元外部(通过公平方式)是不可访问的.

那不公平的意思呢?好吧,有些公共方法TCharacter可以打电话IsLatin1.即使IsLatin1声明了inline,似乎这些方法是使用调用语句而不是内联代码编译的.也许这是因为它们的调用发生在同一单元或相同类型中,并且内联引擎无法内联.

无论如何,我要去的地方是你可以在运行时反汇编其中一个调用.为了论证,我们考虑一下IsControl:

class function TCharacter.IsControl(C: Char): Boolean;
begin
  if IsLatin1(C) then
    Result := InternalGetLatin1Category(C) = TUnicodeCategory.ucControl
  else
    Result := InternalGetUnicodeCategory(UCS4Char(C)) = TUnicodeCategory.ucControl;
end;
Run Code Online (Sandbox Code Playgroud)

它的第一幕是打电话IsLatin1.编译后的代码如下所示:

System.Character.pas.517: 
00411135 C3               ret 
00411136 8BC0             mov eax,eax
TCharacter.IsControl:
00411138 53               push ebx
00411139 8BD8             mov ebx,eax
System.Character.pas.533: 
0041113B 8BC3             mov eax,ebx
0041113D E852FFFFFF       call TCharacter.IsLatin1
00411142 84C0             test al,al
00411144 740F             jz $00411155

所以,你可以做到以下几点:

  1. 取地址TCharacter.IsControl.
  2. 反汇编该地址的代码,直到找到第一call条指令.
  3. 解码该call指令以找到目标地址,这就是IsLatin1可以找到的地方.

我并没有远程提倡这一点IsLatin1.它是一个如此简单的功能,并且不会发生变化,重新实现它肯定会更好.但是对于更复杂的情况,可以使用此方法.

而且我也没有声称原创性.我从madExcept源代码中学到了这个技术.


好的,@ LU RD有资源找到了证明我错的方法.祝贺你.我所说的static方法是准确的,但是@LU RD使用了非常熟练的技巧来引入非静态类方法,并通过这种方式破解私有成员.

我想通过展示如何使用两个帮助程序使用原始名称公开功能来进一步回答他的答案:

unit CharacterCracker;

interface

uses
  System.Character;

type
  TCharacterHelper = class helper for TCharacter
  public
    class function IsLatin1(C: Char): Boolean; static; inline;
  end;

implementation

type
  TCharacterCracker = class helper for TCharacter
  public
    class function IsLatin1Cracker(C: Char): Boolean; inline;
  end;

class function TCharacterCracker.IsLatin1Cracker(C: Char): Boolean;
begin
  Result := TCharacter.IsLatin1(C); // resolves to the original method
end;

class function TCharacterHelper.IsLatin1(C: Char): Boolean;
begin
  Result := TCharacter.IsLatin1Cracker(C);
end;

end.
Run Code Online (Sandbox Code Playgroud)

您可以使用此单元,并且在单元外部激活的唯一帮助程序是在接口部分中声明的帮助程序.这意味着您可以编写如下代码:

{$APPTYPE CONSOLE}

uses
  System.Character,
  CharacterCracker in 'CharacterCracker.pas';

var
  c: Char;

begin
  c := #42;
  Writeln(TCharacter.IsLatin1(c));
  c := #666;
  Writeln(TCharacter.IsLatin1(c));
  Readln;
end.
Run Code Online (Sandbox Code Playgroud)