3

非常に役立つ可能性があるが、保護されたスコープのために利用できないメソッドを持つクラスがあるとします。

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;

私は、そのような方法に手を差し伸べる 2 つの昔ながらの方法を知っています。どちらも、継承と型キャストが関係しています。どちらのアプローチも、基本的なケース #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;

知りたい:

  • クラスヘルパーの力を適用して同じ結果を得るにはどうすればよいですか?
  • クラスヘルパーでもプライベートメソッドを呼び出すことは可能ですか?
  • 内部表現ではなく、どのクラスヘルパーがクラススコープを拡張するという事実により、ケース#1とケース#2の間に違いはありますか?
  • 再帰の危険を冒さずにクラスヘルパーで再導入されたメソッドから元のメソッドを呼び出す方法は?

また、不安全に関する発言についてはコメントをお願いしTIntroducerます。

4

1 に答える 1

6

次のようにヘルパーを使用できます。

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.

これらは、別のユニットで宣言することもできます。たとえば、次のようになります。

unit Unit2;

interface

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

implementation

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

end.

そして別々に

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.

そしてテストするには:

program Project1;


{$APPTYPE CONSOLE}

uses
  //Unit2,  // either or
  Unit3;

var
  foo : TSpecific2;
begin
  foo := TSpecific2.Create;
  foo.ExposedUseful;
  Readln;
end.

代わりに基底クラスのヘルパーを作成すると、プライベート メンバーを同様の方法で公開できます。ただし、別のユニットの場合はキャストが必要です。例えば ​​:

// 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;

ポリモーフィズムに関しては、実際に自分でテストできます。ヘルパーは、インスタンスが派生するどの子孫クラスにも適用されます。

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

// ...

procedure TSpecific1.Useful;
begin
  WriteLn('specific 1');
end;

どこ

TSpecific2 = class(TSpecific1)
end;

specific 1上記の基本クラスのヘルパーで呼び出されると、出力が生成されます。

メモ
Delphi 10.1 Berlin から、クラス ヘルパーは strict protected、strict private、または private メンバーにアクセスできなくなりました。この「機能」は、実際にはエンバカデロがベルリンで修正したコンパイラのバグでした。
ヘルパーを使用してプレーンな保護されたメンバーにアクセスすることは引き続き可能です。

于 2014-09-18T17:06:16.190 に答える