Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I call static private class method with class helper?

Tags:

delphi

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?

like image 692
Free Consulting Avatar asked Dec 08 '22 09:12

Free Consulting


1 Answers

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

Yes. By introducing a non-static function call through a new class function. The trick here is to use the helper ability to access all members through Self. See Access a strict protected property of a Delphi class? and How do I use class helpers to access strict private members of a class?. This is done by calling a private helper non-static function from the new class function, where Self can be resolved.

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;

You cannot use the original method name though, but still the original class function can be called this way.


Oops, David showed a way to expand this idea to use the original name. That can be a versatile trick to have in the toolbox.


Just to refer to what documentation has to say about this:

Ordinary Class Methods:

You can use Self to call constructors and other class methods, or to access class properties and class fields.

Class Static Methods:

Unlike ordinary class methods, class static methods have no Self parameter at all.

Note: Records can only have static class methods, unlike classes.

Class and Record Helpers:

You can use the helper any place where you can legally use the extended class or record. The compiler's resolution scope then becomes the original type, plus the helper.

...

The visibility scope rules and memberList syntax are identical to that of ordinary class and record types.

You can define and associate multiple helpers with a single type. However, only zero or one helper applies in any specific location in source code. The helper defined in the nearest scope will apply. Class or record helper scope is determined in the normal Delphi fashion (for example, right to left in the unit's uses clause).


As you noted above, records can only have static class methods. So if you wanted to "reintroduce" a private class method in a record, here is a solution (based on David's technique):

Suppose we have:

Type
  TTestRec = record
  private
    class Function IsLatin1(C: Char): Boolean; static; inline;
  end;

And add a helper in a new unit:

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.
like image 112
13 revs Avatar answered Feb 15 '23 22:02

13 revs