我可以使用类帮助调用静态私有类方法吗? [英] Can I call static private class method with class helper?

查看:159
本文介绍了我可以使用类帮助调用静态私有类方法吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

特别是,我觉得在 TCharacter.IsLatin1 中需要 private

  type 
TCharacterHelper = TCharacter的类助手
public
类函数IsLatin1(C: Char):Boolean;静态的;一致;
结束

类函数TCharacterHelper.IsLatin1(C:Char):Boolean;
begin
结果:= Ord(C)<= $ FF;
结束

这种单线方法可以在字面上没有时间重新实现,但最好离开



有没有办法将此方法重新引入到 public 可见性?

解决方案

请参阅下面的更新



众所周知,帮手确实破坏了私人的视野。所以,私人成员从类助手可见。但是,这种行为并不会扩展到静态成员,所以 c $ c> TCharacter.IsLatin1 在被声明的单位之外是无法访问的(以公平的方式)。
$ b

不公平意味着什么?那么一些 TCharacter 的公共方法可以调用 IsLatin1 。即使 IsLatin1 被声明为 inline ,似乎这些方法是使用调用语句编译而不是内联的代码。也许这是因为它们的呼叫发生在同一个单元或同一类型中,并且内联引擎不能内联。



无论如何,我要去的是您可以在运行时反汇编其中一个呼叫。为了争论,我们来考虑 IsControl

  class function TCharacter。 IsControl(C:Char):Boolean; 
begin
如果IsLatin1(C)然后
结果:= InternalGetLatin1Category(C)= TUnicodeCategory.ucControl
else
结果:= InternalGetUnicodeCategory(UCS4Char(C))= TUnicodeCategory.ucControl;
结束

它的第一个动作是调用 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. 在该地址处拆卸代码,直到找到第一个调用指令。

  3. 解码调用指令以找到目标地址,这就是可以找到 IsLatin1 的地方。

我不是主张为 IsLatin1 。这是一个简单的功能,不会改变,它更好地重新实现它。但是对于更复杂的情况,可以使用这种方法。



而且我也没有声称是独创性的。我从madExcept源代码中学到了这种技术。






OK,@LU RD巧妙地找到了一种证明我错误的方法。恭喜你我所说的关于 static 方法的方法是准确的,但是@LU RD使用非常娴熟的技巧来引入非静态类方法,从而破解私有成员。 / p>

我想通过显示如何使用两个帮助者使用原始名称公开功能来进一步回答:

 单位CharacterCracker; 

界面

使用
System.Character;

type
TCharacterHelper = TCharacter的类助手
public
类函数IsLatin1(C:Char):Boolean;静态的;一致;
结束

实现

类型
TCharacterCracker = TCharacter的类帮助
public
类函数IsLatin1Cracker(C:Char):Boolean;一致;
结束

类函数TCharacterCracker.IsLatin1Cracker(C:Char):Boolean;
begin
结果:= TCharacter.IsLatin1(C); //解析为原来的方法
end;

类函数TCharacterHelper.IsLatin1(C:Char):Boolean;
begin
结果:= TCharacter.IsLatin1Cracker(C);
结束

结束。

您可以使用本机,在本机外部活动的唯一助手是在界面部分。这意味着你可以这样编写代码:

  {$ APPTYPE CONSOLE} 

使用
System.Character,
CharacterCracker in'CharacterCracker.pas';

var
c:Char;

begin
c:=#42;
Writeln(TCharacter.IsLatin1(c));
c:=#666;
Writeln(TCharacter.IsLatin1(c));
Readln;
结束。


In particular, I feel the need in TCharacter.IsLatin1 which is private.

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;

This one-liner method can be reimplemented in literally no time, but I'd better off leave the exact implementation details on the vendor's discretion.

Is there any way to "reintroduce" this method to public visibility?

解决方案

See update below

As is widely known, helpers do crack private visibility. So, private members are visible from a class helper. However, this behaviour does not extend to static members, so TCharacter.IsLatin1 is inaccessible (by fair means) outside the unit in which it is declared.

What about unfair means? Well, some public methods of TCharacter do call IsLatin1. And even though IsLatin1 is declared inline, it seems that these methods are compiled with call statements rather than the code inlined. Perhaps that's because they calls occur in the same unit, or the same type, and the inline engine is not capable of inlining.

Anyway, where I am going with this is that you could, at runtime, disassemble one of these calls. For sake of argument, let's consider 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;

Its first act is to call IsLatin1. The compiled code looks like this:

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

So, you could do the following:

  1. Take the address of TCharacter.IsControl.
  2. Disassemble the code at that address until you found the first call instruction.
  3. Decode that call instruction to find the target address, and that's where IsLatin1 can be found.

I'm not remotely advocating this for IsLatin1. It's such a simple function, and not subject to change, that it's surely better to re-implement it. But for more complex situations, this method can be used.

And I'm also not claiming originality. I learnt this technique from the madExcept source code.


OK, @LU RD resourcefully found a way to prove me wrong. Congratulations for that. What I said about static methods is accurate, however, @LU RD used a very adept trick of introducing a non-static class method and by that way crack the private members.

I'd like to take his answer a bit further by showing how to use two helpers to expose the functionality using the original name:

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.

You can use this unit, and the only helper that is active outside the unit is the one declared in the interface section. Which means you can write code like this:

{$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.

这篇关于我可以使用类帮助调用静态私有类方法吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆