正常松开“分配给过程变量的本地过程/功能”限制

时间:2011-02-18 18:13:51

标签: delphi scope procedural

考虑以下测试用例:

{ 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的兄弟姐妹),这会对代码“结构性”产生负面影响。


我正在寻找以最优雅的方式绕过它的方法,最好让编译器发出警告级别的消息。

4 个答案:

答案 0 :(得分:8)

您最好的选择是使用新的匿名方法功能将其声明为reference to procedure,然后您可以将所有内容妥善保存。

type
  TProc = reference to procedure;

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

这解决了Mason通过捕获匿名函数本地的任何变量所描述的问题。

答案 1 :(得分: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),那么您可以使用anonymous methods,这应该能够满足您的需求;你只需要稍微不同的语法。

答案 2 :(得分: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调用编写一些代码,并通过断点查看哪些寄存器在那里使用...

是的 - 在大多数真实的制作案例中,这个技巧只是某种缓和。

答案 3 :(得分:0)

对于记录,我的homebrewn关闭:

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