TObjectのMethodAddressメソッドを使用すれば関数名(文字列)でメソッドをコールできます。
しかし、 MethodAddressメソッドでメソッドのアドレスを取得するには、可視性をpublishedに
する必要があり、 publishedにした瞬間にメソッドの存在が丸わかりになってしまいます。
前回の例のような単純な演算であればまあ良いかと思うのですが、いわゆるFactoryメソッド
とかだと、使用者にその存在を隠したい場合があり、このままではちょっと不完全です。
さて、どうしようか?というのが今回のテーマになります。(2010以降の拡張Rttiを使えば良い
というのはあるのですが、今回は別の方法を考えます。)
ところで、DelphiのUnitのimplementationセクションで定義した型(クラスを含む)は別のユニット
から参照できない仕様となっています。
したがって、クラスの定義を implementation で行えば、外部から処理の存在を隠したうえで
MethodAddressメソッド が使用可能なクラスができそうです。
で、実際に、作ってみました。
先ずは、【メソッドポインタ経由で呼び出されるクラスのサンプル】
- unit Unit2;
- interface
- function CallCalc(CalcName : string; a, b: double) : double;
- implementation
- type TMyCalc = class
- published
- function Add(a,b : double) : double;
- function Subtract(a,b : double) : double;
- end;
- type TMyCalcFunc = function(a,b : double) : double of object;
- { TMyCalc }
- function TMyCalc.Add(a, b: double): double;
- begin
- Result := a + b;
- end;
- function TMyCalc.Subtract(a, b: double): double;
- begin
- Result := a - b;
- end;
- function CallCalc(CalcName : string; a, b: double) : double;
- var
- MyCalc : TMyCalc;
- MyCalcFunc : TMyCalcFunc;
- MethodVar : TMethod;
- begin
- MyCalc := TMyCalc.Create;
- try
- MethodVar.Data := MyCalc;
- MethodVar.Code := MyCalc.MethodAddress(CalcName);
- if Assigned(MethodVar.Code) then
- begin
- MyCalcFunc := TMyCalcFunc(MethodVar);
- Result := MyCalcFunc(a,b);
- end;
- finally
- MyCalc.Free;
- end;
- end;
- end.
publishedの可視性を持つクラスをimplementationセクションに定義しています。
但し、そのままでは、外部のユニットからメソッドをコールできませんので、
外部からのアクセスようにラッパー関数を定義しています。
次に、【関数名を文字列で指定して処理を呼び出すサンプル】
- interface
- uses
- Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
- Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
- type
- TForm1 = class(TForm)
- Button1: TButton;
- Button2: TButton;
- LabeledEdit1: TLabeledEdit;
- LabeledEdit2: TLabeledEdit;
- StaticText1: TStaticText;
- StaticText2: TStaticText;
- procedure OnCalcBtnClick(Sender: TObject);
- private
- { Private 宣言 }
- public
- { Public 宣言 }
- end;
- var
- Form1: TForm1;
- implementation
- {$R *.dfm}
- uses Unit2;
- procedure TForm1.OnCalcBtnClick(Sender: TObject);
- begin
- StaticText2.Caption := FloatToStr(CallCalc(
- TButton(Sender).Caption,
- StrToFloat(LabeledEdit1.Text),
- StrToFloat(LabeledEdit2.Text)));
- end;
- end.
unit2のinterfaceセクションに定義したラッパーを使って処理を呼び出しています。
implementationセクションにクラスを定義することで、外部からその存在を隠しつつ
メソッド名を文字列で指定してメソッドをコールすることが可能です。
但し、この方法ですとクラスそのものも隠れてしまいすのでもう少しなんとかしたい
ところです。
ネストした型宣言が可能なバージョンのDelphiであれば、何とかできそうな気が
しますが、その検証はまた後日・・・
0 件のコメント:
コメントを投稿