特别是,我觉得有必要在TCharacter.IsLatin1
这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;
Run Code Online (Sandbox Code Playgroud)
这种单线方法可以在短时间内重新实现,但我最好留下供应商自行决定的确切实现细节.
有没有办法将这种方法"重新引入" public
可见性?
有没有办法将这种方法"重新引入"公众可见度?
是.通过一个新的类函数引入一个非静态函数调用.这里的技巧是使用帮助程序能力来访问所有成员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)
但是,您不能使用原始方法名称,但仍可以通过这种方式调用原始类函数.
哎呀,大卫展示了一种方法来扩展这个想法以使用原始名称.这可能是工具箱中的一个多功能技巧.
只是想参考哪些文档对此有所说明:
您可以使用Self来调用构造函数和其他类方法,或者访问类属性和类字段.
与普通的类方法不同,类静态方法根本没有Self参数.
注意:与类不同,记录只能有静态类方法.
您可以在合法使用扩展类或记录的任何地方使用帮助程序.然后编译器的解析范围变为原始类型,再加上帮助程序.
...
可见范围规则和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)
请参阅下面的更新
众所周知,帮助者会破坏私人知名度.因此,私人成员可以从类助手中看到.但是,此行为不会扩展到静态成员,因此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
所以,你可以做到以下几点:
TCharacter.IsControl
.call
条指令.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)