Delphi Programming / Object Pascal
[掲載 2022年02月13日] [更新 2022年02月13日] Delphi 一般・その他 |
メソッドのポインタと手続きのポインタ |
動作確認等 |
Windows 10 ビルド 19042 + Delphi XE5(UP2) Pro VCL-32 |
クラスの function, procedure (関数,手続き) はメソッドあるいはクラスのメソッドと呼ばれています.本ページのタイトルの「手続き」はクラスのメンバではない手続き型のことです. |
TStringList の LoadFromFile メソッドを使用する例です.テキストファイルを読み込んで TMemo に表示します. |
|
図1
TStringList.LoadFromFile メソッドの使用例 |
unit Unit1;
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;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
//=============================================================================
// TStringList.LoadFromFile メソッドでテキストファイルを読み込む
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LStrList : TStringList;
begin
LStrList := TStringList.Create;
try
LStrList.LoadFromFile('Hoge.txt');
Memo1.Lines.Text := LStrList.Text;
finally
FreeAndNil(LStrList);
end;
end;
end.
TStringList.LoadFromFile メソッドと同じ引数を持つ手続き型の変数を宣言して使用してみます.
コンパイルはエラーとなりまんが,実行時に下図のエラーとなります.
|
|
図2
メソッドをそのままい続きとして使用するとエラーになる |
//=============================================================================
// TStringList.LoadFromFile メソッドでテキストファイルを読み込む
// メソッドを手続きとして宣言
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LStrList : TStringList;
LProc : procedure(const FileName: string);
begin
LStrList := TStringList.Create;
try
LProc := @TStringList.LoadFromFile;
LProc('Hoge.txt');
Memo1.Lines.Text := LStrList.Text;
finally
FreeAndNil(LStrList);
end;
end;
手続き型に of Object を追加して,メソッド型のポインタ
として宣言します.そうすれば正常にメソッドとして動作します. |
|
図3
手続きをメソッドのポインタとして宣言 |
//=============================================================================
// TStringList.LoadFromFile メソッドでテキストファイルを読み込む
// 変数をメソッドのポインタ (オブジェクト) として宣言
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LStrList : TStringList;
LProc : procedure(const FileName: string) of Object;
begin
LStrList := TStringList.Create;
try
LProc := LStrList.LoadFromFile;
LProc('Hoge.txt');
Memo1.Lines.Text := LStrList.Text;
finally
FreeAndNil(LStrList);
end;
end;
手続き型の引数に,コントロール自身を示す引数を追加します.この手続きのポインタでも正常に動作します.
クラスのメソッドは,内部的にはこのような暗黙の引数を使用するように処理されます. |
//=============================================================================
// TStringList.LoadFromFile メソッドでテキストファイルを読み込む
// 変数を暗黙の引数を使用した手続きとして宣言
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LStrList : TStringList;
LProc : procedure(Self: TStringList; const FileName: string);
begin
LStrList := TStringList.Create;
try
LProc := @TStringList.LoadFromFile;
LProc(LStrList, 'Hoge.txt');
Memo1.Lines.Text := LStrList.Text;
finally
FreeAndNil(LStrList);
end;
end;
上の例のように,メソッドは手続きとして宣言して実行することができます.
このことを利用して,コントロールのイベントを手続きに置き換えることができます.イベントはプロパティであり型があります.また,ローカルで宣言しても意味がありません.そのような場合,TMethod を使用します.その例です. |
|
図4
実行結果 |
unit Unit1;
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;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
procedure OnMouseMoveEvent(Self: TObject; Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure OnClickEvent(Self: TObject; Sender: TObject);
var
Form1: TForm1;
implementation
{$R *.DFM}
//=============================================================================
// Memo1 (TMmoe) のイベントをクラスのメンバではない手続きに割り当てる例
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LMethod : TMethod;
begin
LMethod.Code := @OnMouseMoveEvent;
// Data メンバは,第 1 引数の Self を使用しなければ nil でもよい
// クラス以外で設定する時は nil にでもしておく
// 必要ならば実際のオブジェクトを渡す
LMethod.Data := Self;
Memo1.OnMouseMove := TMouseMoveEvent(LMethod);
LMethod.Code := @OnClickEvent;
LMethod.Data := Self;
Memo1.OnClick := TNotifyEvent(LMethod);
end;
//-----------------------------------------------------------------------------
// OnMouseMove イベント用の手続き
//-----------------------------------------------------------------------------
procedure OnMouseMoveEvent(Self: TObject; Sender: TObject; Shift: TShiftState; X,
Y: Integer);
var
LText : string;
begin
LText := 'X : ' + IntToStr(X) + ' Y : ' + IntToStr(Y);
Form1.Memo1.Lines.Add(LText);
end;
//-----------------------------------------------------------------------------
// OnClick イベント用の手続き
//-----------------------------------------------------------------------------
procedure OnClickEvent(Self: TObject; Sender: TObject);
begin
ShowMessage('Clicked !! ' + TControl(Sender).Name);
end;
end.
TNotifyEven 型のイベントの場合は第 1 引数を省略しても動作します.この場合,Data メンバの値 (ポインタ) は Sender になります.したがって,Data メンバの値を nil にすると,イベントが発生したコントロールを特定できなくなります.
第 1 引数を省略しなければ,Sender は常にイベントの発生コントロールです.第 1 引数を省略しない方が便利と言えます. |
|
図5
実行結果 |
unit Unit1;
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;
Panel1: TPanel;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
procedure OnClickEvent(Sender: TObject);
var
Form1: TForm1;
implementation
{$R *.DFM}
//=============================================================================
// Panle1 の OnClick イベントをクラスのメンバではない手続きに割り当てる例
//
// type
// TNotifyEvent = procedure(Sender: TObject) of object;
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LMethod : TMethod;
begin
LMethod.Code := @OnClickEvent;
LMethod.Data := Panel1;
Panel1.OnClick := TNotifyEvent(LMethod);
end;
//-----------------------------------------------------------------------------
// OnClick イベント用の手続き
//-----------------------------------------------------------------------------
procedure OnClickEvent(Sender: TObject);
begin
ShowMessage('Clicked !! ' + TControl(Sender).Name);
end;
end.
|