function TForm1.DoIt(i:integer):integer;
begin
end;
procedure TForm1.Main;
//-------------------------------------------------------
procedure CallIt;
begin
TAsyncCalls.Invoke(
procedure
var i:integer;
begin
For i := 0 to 10 do
If i < 11
then TAsyncCalls.Invoke<integer>(DoIt,i));
end);
end;
//-------------------------------------------------------
begin
CallIt;
end;
现在我想将函数DoIt移动到Main作为CallIt旁边的嵌套函数:
procedure TForm1.Main;
//-------------------------------------------------------
function DoIt(i:integer):integer;
begin
end;
//-------------------------------------------------------
procedure CallIt;
begin
TAsyncCalls.Invoke(
procedure
var i:integer;
begin
For i := 0 to 10 do
If i < 11
then TAsyncCalls.Invoke<integer>(DoIt,i));
end);
end;
//-------------------------------------------------------
begin
CallIt;
end;
以上代码(自然)不起作用。尽管我unterstand Invoke需要一个方法作为参数,嵌套函数不是一个。
Invoke需要TAsyncCallArgGenericMethod:
class function Invoke<T>(Event: TAsyncCallArgGenericMethod<T>; const Arg: T): IAsyncCall; overload; static;
TAsyncCallArgGenericMethod<T> = function(Arg: T): Integer of object;
我已收到将TAsyncCallArgGenericMethod转换为引用的提示:
TAsyncCallArgGenericMethod<T> = reference to function(Arg: T): Integer;
虽然我有一般概念(即幻觉),但我理解这个概念,我无法生成工作代码。
答案 0 :(得分:2)
现在我想将函数DoIt移动到Main作为CallIt旁边的嵌套函数:
您不能从包含它的函数外部调用嵌套函数 - 因为嵌套函数需要访问外部(包含)函数局部变量,这些函数仅在执行包含函数的代码时存在。
即使特定的嵌套函数没有评估它们访问这些局部变量的权限 - 它具有这些权限,编译器应该能够为此生成所有的低级脚手架。
特别是在您的代码段中,您无法从TForm1.Main.DoIt
本身之外调用TForm1.Main
。所以你不能把它引用并传递给像AsyncCall
调度员这样的外部体。
它不取决于您是使用procedure of object
还是reference to procedure
还是任何其他类型 - 嵌套函数的基本属性是&#34;存在&#34;只在本地到包含函数,只有在外部函数运行时才能运行。 AsyncCall
很可能会在TForm1.Main
退出时尝试运行该函数,因此TForm1.Main.DoIt
所需的局部变量堆栈框架将不存在。
你必须找到其他方式来打包&#34;打包&#34;这些功能在一起,嵌套函数不会在这里做。
例如,可以尝试使用高级记录。 尝试以某种方式安排它:
type
TForm1 = class(TForm)
....
private
type Dummy = record
procedure CallIt;
procedure DoIt(const i:integer);
end;
end;
....
//-------------------------------------------------------
procedure TForm1.Dummy.CallIt;
begin
TAsyncCalls.Invoke(
procedure
var i:integer;
begin
For i := 0 to 10 do
If i < 11
then TAsyncCalls.Invoke<integer>(DoIt,i));
end);
end;
procedure TForm1.Dummy.DoIt(const i:integer);
begin
end;
procedure TForm1.Main;
var d: Dummy;
begin
d.CallIt;
end;
另外,我认为你的方法在这里是错误的:你会立即形成许多线程,耗尽你的操作系统资源。 我建议您使用OmniThreadLibrary,其中有高级Parallel-Loop和Collection-Pipeline概念。它们将为您提供自动线程池管理的好处,因此您只需拥有CPU可以承受的许多工作线程,使您的程序适应它将要运行的任何硬件。
答案 1 :(得分:1)
我也可能有一种错觉,我理解这些事情(也就是我可能是错的),所以请用一点点盐,但这是我对它的看法。
嵌套函数可以访问调用函数可用的所有参数(包括self),但没有“隐藏”参数(它不需要任何参数)。另一方面,类函数有一个隐藏参数(称为“self”),函数访问该参数以查找实际调用该函数的对象。因此签名完全不同。
如果你回到过去C ++是一个解释器的时代,像C ++中的Fred.Main(x,y)这样的东西会被翻译成像C中的Main(Fred,x,y)这样的东西。我只包括这个说明隐藏参数的工作原理。
所以结果就是你无法做你想要做的事情,因为通过在你的Main函数中移动DoIt,你完全改变它的签名,以及它的工作原理。
答案 2 :(得分:0)
我真的不能离开它,因为出于某种原因,我真的把它咬进了它。现在,这是一个解决方案。不是我推荐的解决方案,而是解决方案。
大约4年前,有关stackoverflow的讨论here。大卫引用了documentation并继续说道:
如果我没记错的话,一个额外的隐藏参数会被传递给嵌套函数,并带有指向封闭堆栈帧的指针。如果没有引用封闭环境,则在32位代码中省略。
SertaçAkyüz显然在汇编代码中喋喋不休地报道:
这是一个隐含的参数好吧!编译器假定它在'rcx'中有它的东西,函数的参数在'rdx'和'r8',而实际上没有'它的东西',参数在'rcx'和'rdx'。 / p>
这似乎完成了整个事情。
但是,有这样的文字:How to pass a nested routine as a procedural parameter (32 bit)。如果您考虑文档,这是一个相当令人惊讶的标题。这导致了以下代码:
{unit AsyncCalls;}
TAsyncCalls = class(TObject)
private
type
…
//TAsyncCallArgGenericMethod<T> = function(Arg: T): Integer of object;
TAsyncCallArgGenericMethod<T> = reference to function(Arg: T): Integer;
uses … ,AsyncCalls,AsyncCallsHelper;
procedure TForm1.Main;
//-------------------------------------------------------
function DoIt(i:integer):integer;
begin
Result := i;
end;
//-------------------------------------------------------
procedure CallIt;
var p:Pointer;
begin
p := @DoIt;
TAsyncCalls.Invoke(
procedure
var i:integer;
begin
For i := 0 to 10 do
If i < 11 then
AsyncHelper.AddTask(TAsyncCalls.Invoke<integer>(p,i));
end);
end;
//-------------------------------------------------------
begin
CallIt;
end;
此代码有效。正如我之前提到的,我不建议使用它,但它有效。我在寻找解决方案的过程中学到了很多东西,我现在认为这是一个主要的好处。