クラス内の定義関数について

解決


ahsan  2009-12-12 18:37:16  No: 36421

別のフォームのクラスのPublic内に定義した関数を呼び出す際、
誤ってそのフォームをCreateせずに呼び出したにもかかわらず、
正常に動作します。別途以下のようなサンプルを作成し、実行
してみましたが、やはり、正常に動作しています。
Createをしなくても呼び出せるのでしょうか?
なお、Form2はプロジェクトオプションで、使用可能フォーム
の方へ移動しています。

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;
type
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
uses
  Unit2;
{$R *.dfm}
procedure TForm1.FormShow(Sender: TObject);
begin
  Form2:=nil;
end;
//本当は、Button1をClickする前に
//Form2:=TForm2.Create(Application);とするつもり
//だったのを、忘れてしまった。
procedure TForm1.Button1Click(Sender: TObject);
var
  res : integer;
begin
  res:=Form2.sub(100,200);
  Label1.Caption:=IntToStr(res);
end;
end.

unit Unit2;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;
type
  TForm2 = class(TForm)
    Label1: TLabel;
  private
    { Private declarations }
  public
    { Public declarations }
    function sub(a,b:integer):integer;
  end;
var
  Form2: TForm2;
implementation
{$R *.dfm}
function TForm2.sub(a,b:integer):integer;
begin
  Result:=a+b;
end;
end.


monaa  2009-12-12 19:20:20  No: 36422

プロジェクト->オプション->フォーム  
TForm2が自動生成の対象になってるだけでしょう。
プロジェクト->ソースの表示
でプロジェクトファイル内で生成されていると思われます。


ahsan  2009-12-12 19:57:46  No: 36423

monaaさん、早速ありがとうございます。
自動生成の対象から、使用可能フォームへ移動しています。
プロジェクト->ソースの表示で
プロジェクトファイル内を表示しましたが、
以下のように表示されました。
program Project1;

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1},
  Unit2 in 'Unit2.pas' {Form2};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.


それは  2009-12-12 20:16:10  No: 36424

>実行してみましたが、やはり、正常に動作しています。
>Createをしなくても呼び出せるのでしょうか?
メソッドだけの呼び出しなら一応問題なし。
ただし、Form2にメンバー変数をおいて使ったトタンに
それは破綻する。


ahsan  2009-12-12 20:58:06  No: 36425

それはさんありがとうございます。
なるほど、subを
function TForm2.sub(a,b:integer):integer;
begin
  Label1.Caption:='';
  Result:=a+b;
end;
のように、Lable1.Caption:='';を追加するとこけました。
クラスもtype宣言なので、Createされて、はじめて、
メソッドも含め、すべてが使えるようになると思っていました。
なぜ問題なにのか?私にはよくわからないのですが、
Createしてから使うのが王道ですよね。


jazzin  2009-12-13 00:02:05  No: 36426

蛇足ですが、
「どこかのクラスに所属させたいが、今回のようにそのクラスの変数には触れない」
という関数は、クラスメソッドとすることでインスタンスを生成することなく使用できます。
定義はprocedureやfunctionの前に"class"と書き、使う際は変数ではなくクラス名から呼び出します。
今回の例で言えばこの様な感じです。

type
  TForm2 = class(TForm)
    ...
    class function sub(a,b:integer):integer; { static; }
  end;

class function TForm2.sub(a,b:integer):integer;
begin
  Result:=a+b;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  res : integer;
begin
  res:=TForm2.sub(100,200); // Form2ではなくTForm2
  Label1.Caption:=IntToStr(res);
end;


ahsan  2009-12-13 01:03:39  No: 36427

jazzinさん、
class定義を追加すると、Label1.Caption:='';で
コンパイルエラーが出ましたので以下のように修正しました。
class function TForm2.sub(a,b:integer):integer;
begin
//Label1.Caption:='';コンパイルエラーなのでコメントにした
  Result:=a+b;
end;
私の方法でも、jazzinさんのクラスメソッドの方法でも、クラス変数に
触れない限りは、実行すると同じ結果が得られます。
しかし、私の方法では、subが自身をCreateされているか否か
わかならいので、触れてしまった場合、実行時エラーとなり、
将来的にバグが見つけにくくなりそうです。
クラスメソッドの方法では、コンパイル時エラー
となるので、バグになるまえに発見できると理解しました。
ありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加