特别是,我觉得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;
这种单线方法可以在短时间内重新实现,但我最好留下供应商自行决定的确切实施细节。
有没有办法将此方法“重新引入”public
可见度?
答案 0 :(得分: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;
虽然您不能使用原始方法名称,但仍可以通过这种方式调用原始类函数。
哎呀,大卫展示了一种方法来扩展这个想法以使用原始名称。这可能是工具箱中的一个多功能技巧。
仅提及有关此文件的说明:
您可以使用Self调用构造函数和其他类方法,或访问类属性和类字段。
与普通的类方法不同,类静态方法根本没有Self参数。
注意:记录只能使用静态类方法,与类不同。
您可以在合法使用扩展类或记录的任何地方使用帮助程序。然后编译器的解析范围变为原始类型,再加上帮助程序。
...
可见范围规则和memberList语法与普通类和记录类型的语法相同。
您可以使用单一类型定义和关联多个帮助程序。但是,源代码中的任何特定位置只应用零或一个助手。最近范围中定义的帮助程序将适用。类或记录助手范围以正常的Delphi方式确定(例如,在单位的使用条款中从右到左)。
如上所述,记录只能有静态类方法。因此,如果您想在记录中“重新引入”私有类方法,这里有一个解决方案(基于David的技术):
假设我们有:
Type
TTestRec = record
private
class Function IsLatin1(C: Char): Boolean; static; inline;
end;
在新单元中添加帮助器:
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.
答案 1 :(得分: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;
它的第一个动作是致电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.
您可以使用此单元,并且在单元外部活动的唯一帮助程序是在接口部分中声明的帮助程序。这意味着您可以编写如下代码:
{$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.