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),这将对代码“结构化”产生负面影响。


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

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 回答