我可以使用类帮助调用静态私有类方法吗? [英] Can I call static private class method with class helper?
问题描述
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
所以,您可以执行以下操作:
- 取地址
TCharacter.IsControl
。 - 在该地址处拆卸代码,直到找到第一个
调用
指令。 - 解码
调用
指令以找到目标地址,这就是可以找到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:
- Take the address of
TCharacter.IsControl
. - Disassemble the code at that address until you found the first
call
instruction. - Decode that
call
instruction to find the target address, and that's whereIsLatin1
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屋!