8

次のテストケースを考えてみましょう:

{ CompilerVersion = 21 }
procedure Global();

  procedure Local();
  begin
  end;

type
  TProcedure = procedure ();
var
  Proc: TProcedure;
begin
  Proc := Local;  { E2094 Local procedure/function 'Local' assigned to procedure variable }
end;

13 行目で、コンパイラは ERROR レベルのメッセージを出力し、そのようなローカル プロシージャの使用のすべてのケースを禁止します。「公式の」解決策は、コードの「構造化」に悪影響を与えるLocal外側のスコープにシンボルを昇格させることです(つまり、シンボルを の兄弟にする)。Global


私は、できればコンパイラに WARNING レベルのメッセージを出力させる、最も適切な方法でそれを回避する方法を探しています。

4

4 に答える 4

8

最善の策はreference to procedure、新しい匿名メソッド機能を使用していると宣言することです。そうすれば、すべてを適切にカプセル化したままにすることができます。

type
  TProc = reference to procedure;

procedure Outer;
var
  Local: TProc;
begin
  Local := procedure
    begin
      DoStuff;
    end;
  Local;
end;

これは、無名関数にローカルな変数をキャプチャすることで、Mason が説明する問題を回避します。

于 2011-02-18T18:25:14.827 に答える
5

これができない理由は次のとおりです。

type
  TProcedure = procedure ();

function Global(): TProcedure;
var
  localint: integer;

  procedure Local();
  begin
    localint := localint + 5;
  end;

begin
  result := Local;
end;

ローカル プロシージャは、外部ルーチンの変数スコープにアクセスできます。ただし、これらの変数はスタック上で宣言され、外部プロシージャが戻ると無効になります。

ただし、CompilerVersion 21 (Delphi 2010) を使用している場合は、探していることを実行できる匿名メソッドを使用できます。少し異なる構文が必要なだけです。

于 2011-02-18T18:25:16.580 に答える
1

D7 以前でローカル プロシージャを本当に使用する必要がある場合は、次のトリックを使用できます。

procedure GlobalProc;
var t,maxx:integer; itr,flag1,flag2:boolean; iterat10n:pointer;
    //Local procs:
    procedure iterat10n_01;begin {code #1 here} end;
    procedure iterat10n_10;begin {code #2 here} end;
    procedure iterat10n_11;begin {code #1+#2 here} end;
begin
    //...
    t:=ord(flag2)*$10 or ord(flag1);
    if t=$11 then iterat10n:=@iterat10n_11
      else if t=$10 then iterat10n:=@iterat10n_10
        else if t=$01 then iterat10n:=@iterat10n_01
          else iterat10n:=nil;
    itr:=(iterat10n<>nil);
    //...
    for t:=1 to maxx do begin
        //...
        if(itr)then asm
            push ebp;
            call iterat10n;
            pop ecx;
        end;
        //...
    end;
    //...
end;

ただし、問題は、アドレスレジスタがマシンによって異なる可能性があることです。そのため、ローカル proc 呼び出しを使用してコードを記述し、そこで使用されているレジスタをブレークポイントで確認する必要があります...

そして、そうです - ほとんどの実際のプロダクションのケースでは、このトリックはある種の緩和策にすぎません。

于 2016-02-29T08:10:54.923 に答える
0

記録のために、私の自作の閉鎖:

{ this type looks "leaked" }
type TFunction = function (): Integer;

function MyFunction(): TFunction;

  {$J+ move it outside the stack segment!}
  const Answer: Integer = 42;

  function Local(): Integer;
  begin
    Result := Answer;
    { just some side effect }
    Answer := Answer + Answer div 2;
  end;

begin
  Result := @Local;
end;


procedure TForm1.FormClick(Sender: TObject);
var
  Func: TFunction;
  N: Integer;
begin
  { unfolded for clarity }
  Func := MyFunction();
  N := Func();
  ShowMessageFmt('Answer: %d', [N]);
end;
于 2011-02-18T20:56:55.610 に答える