如何使用类助手调用受​​保护的方法?

Fre*_*ing 3 delphi oop class-helpers

假设我们有一个方法的类可能非常有用,但是由于受保护范围不可用:

unit Sealed;

interface

type
  TGeneral = class(TObject)
    { this method is useful, but not available }
    protected procedure Useful; virtual;
  end;

  TSpecific1 = class(TGeneral)
    { some descendants override `Useful` method }
    protected procedure Useful; override;
  end;

  TSpecific2 = class(TGeneral)
    { and some dont, but inherit `Useful`ness from the parent }
  end;
Run Code Online (Sandbox Code Playgroud)

我知道有两种老派的方式来接触这种方法,两者都涉及继承和类型转换.两种方法都应该与基本情况#1和高级多态情形#2相同.

program CallingSite;

uses Sealed;

function GetInstance: TGeneral;
begin
  { !PSEUDO! makes compiler happy about the rest of code }
  // depending on use case supposed to return an instance of `TGeneral`
  // or any of its descendants - `TSpecific1`, `TSpecific2`
end;

type
  { this makes a current module a "friend" for `TGeneral` }
  TFriend = class(TGeneral)
  end;

procedure Case1;
var
  { holds an instance of `TGeneral` }
  General: TGeneral;
begin
  General := GetInstance;
  { protected method is available for "friend" via static cast }
  TFriend(General).Useful;  // compiles!
end;

type
  TIntroducer = class(TGeneral)
  { this "reintroduces" `Useful` method to public scope }
  public procedure Useful; override;
  // this approach ought to work even with strict protected methods
  // !!! but I THINK it is UNSAFE to use on virtual and/or dynamic methods
  end;

procedure TIntroducer.Useful;
begin
  { and calls `Useful` via wrapper }
  inherited;
end;

procedure Case2;
var
  { polymorphic instance of any `TGeneral`'s descendant }
  Specific: TGeneral;
begin
  Specific := GetInstance;
  { protected method is callable via public wrapper, static cast again }
  TIntroducer(Specific).Useful; // compiles!
end;
Run Code Online (Sandbox Code Playgroud)

我想知道:

  • 如何利用阶级助手的力量达到同样的效果?
  • 是否可以使用类助手调用私有方法?
  • 由于什么类助手增加了类范围而不是内部表示,因此情况#1和情况#2之间会有什么不同?
  • 如何从类助手重新引入的方法中调用原始方法,而不会冒递归的风险?

另外,请评论关于TIntroducer不安全的评论.

J..*_*... 5

您可以使用帮助器:

unit Unit2;

interface

type
  TGeneral = class(TObject)
    protected procedure Useful; virtual;
  end;

  TSpecific2 = class(TGeneral)
  end;

  TSpecificHelper = class helper for TGeneral
  public
    procedure ExposedUseful;
  end;

implementation

procedure TGeneral.Useful;
begin
  WriteLn('general');
end;

procedure TSpecificHelper.ExposedUseful;
begin
  Useful;
end;    

end.
Run Code Online (Sandbox Code Playgroud)

这些甚至可以在单独的单元中声明,例如:

unit Unit2;

interface

type
  TGeneral = class(TObject)
    protected procedure Useful; virtual;
  end;

implementation

procedure TGeneral.Useful;
begin
  WriteLn('general');
end;

end.
Run Code Online (Sandbox Code Playgroud)

并分开

unit Unit3;

interface
uses
  Unit2;
type
  TSpecific2 = class(TGeneral)
  end;

  TSpecificHelper = class helper for TGeneral
  public
    procedure ExposedUseful;
  end;

implementation

procedure TSpecificHelper.ExposedUseful;
begin
  Useful;
end;

end.
Run Code Online (Sandbox Code Playgroud)

并测试:

program Project1;


{$APPTYPE CONSOLE}

uses
  //Unit2,  // either or
  Unit3;

var
  foo : TSpecific2;
begin
  foo := TSpecific2.Create;
  foo.ExposedUseful;
  Readln;
end.
Run Code Online (Sandbox Code Playgroud)

如果您为基类创建帮助程序,则可以以类似的方式公开私有成员.如果在不同的单位,则需要演员.例如 :

// in Unit2
TGeneral = class(TObject)
    private
      procedure AlsoUseful;
    protected
      procedure Useful; virtual;
  end;

//in Unit3

  TSpecificHelper = class helper for TGeneral
  public
    procedure ExposedUseful;
    procedure ExposedAlsoUseful;
  end;

// ...
implementation

procedure TSpecificHelper.ExposedAlsoUseful;
begin
  TGeneral(self).AlsoUseful;
end;
Run Code Online (Sandbox Code Playgroud)

至于多态性,你真的可以自己测试一下.帮助程序将应用于您的实例派生自的任何后代类:

  TSpecific1 = class(TGeneral)
    protected
      procedure Useful; override;
  end;

// ...

procedure TSpecific1.Useful;
begin
  WriteLn('specific 1');
end;
Run Code Online (Sandbox Code Playgroud)

哪里

TSpecific2 = class(TSpecific1)
end;
Run Code Online (Sandbox Code Playgroud)

specific 1使用上面基类的帮助程序调用时会产生输出.

注意
从Delphi 10.1 Berlin开始,类帮助程序无法再访问严格受保护的严格私有或私有成员.这个"功能"实际上是Embarcadero现在在柏林修复的编译器错误.
仍然可以使用帮助者访问受保护的普通成员.