考虑以下测试用例:
{ 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
的兄弟姐妹),这会对代码“结构性”产生负面影响。
我正在寻找以最优雅的方式绕过它的方法,最好让编译器发出警告级别的消息。
答案 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;