Delphi 2009中匿名过程的转换

5
以下代码(仅用于演示问题)在Delphi 2010中编译并运行正常。但在Delphi 2009中,编译器会出现“E2035实际参数不足”的错误提示。
program Project50;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
  TMyProc = reference to procedure(param: integer);

var
  a: TProc;
  b: TMyProc;

begin
  b := procedure (param: integer)
    begin
    end;
  a := TProc(b); // <-- [DCC Error] Project50.dpr(19): E2035 Not enough actual parameters
end.

我只发现了一个非常丑陋的解决方法来解决这个问题(a:TProc absolute b)。是否有人知道更好的解决编译器缺陷的方法?

[TProc字段实际上隐藏在一个记录内,该记录可以存储各种“可执行”代码 - TProcedure、TMethod和TProc。强制转换用于将特定的匿名过程存储到此字段中。]

3个回答

2
诀窍在于不做太多的事情。
a := TProc(b);

但是
TMyProc(a) := b;

以下是一个在D2009中编译和工作的示例项目。

program Project51;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
  TMyProc = reference to procedure(var param: integer);

  TStorage = record
    FDelegate: TProc;
  end;

var
  a    : TMyProc;
  b    : TMyProc;
  param: integer;
  stg  : TStorage;

begin
  b := procedure (var param: integer)
    begin
      param := 2*param;
    end;
//  stg.FDelegate := TMyProc(b); // doesn't compile in Delphi 2009, compiles in Delphi 2010
  TMyProc(stg.FDelegate) := b;
  param := 21;
  TMyProc(stg.FDelegate)(param);
  Writeln(param);
  Readln;
end.

然而,如果转换为局部变量,则此方法不起作用。
var
  p: TProc;
  a: TMyProc;

TMyProc(p) := a; // this will not compile

越来越好奇了。


1

我发现了一个技巧 #2:

program Project1;

{$APPTYPE CONSOLE}


uses
  SysUtils;

type
  TMyProc = reference to procedure(param: integer);

var
  a: TProc;
  b: TMyProc;

begin
  b := procedure (param: integer)
    begin
      Writeln('asdf');
    end;
  PPointer(@a)^ := PPointer(@b)^;
  a;
  readln;
end.

我不确定你试图通过将带有参数的TMyProc分配给没有参数的TProc来实现什么?


更新:一个 hack #3(应该增加引用计数器,这个想法是从 System._IntfCopy 偷来的):

procedure AnonCopy(var Dest; const Source);
var
  P: Pointer;

begin
  P:= Pointer(Dest);
  if Pointer(Source) <> nil
    then IInterface(Source)._AddRef;
  Pointer(Dest):= Pointer(Source);
  if P <> nil then
    IInterface(P)._Release;
end;

var
  a: TProc;
  b: TMyProc;

begin
  b := procedure (param: integer)
    begin
      Writeln('asdf');
    end;
  AnonCopy(a, b);
//  PPointer(@a)^ := PPointer(@b)^;
  a;
  readln;
end.

它在测试案例中能正常工作,但在我(稍微更复杂的)案例中却不能。接口引用计数出了问题。我将尝试组合一个更准确的测试案例。TProc只是不同'指向过程'实体的存储区域,这就是我进行类型转换的原因。也许使用泛型可以做得更好... - gabr
@gabr:我已经更新了我的帖子,提出了第三种方法(应该增加接口参考计数器)。 - kludg
感谢您的所有工作,但我刚刚找到了一个非常简单的方法来解决这个问题... - gabr
最后我采用了你的第三种方法。如果你只限制自己使用D2009+,那么泛型是可以的,但我的代码必须在D2007中工作,并且有条件地编译相同类型的泛型/非泛型版本会很混乱。 - gabr
1
如果是这样,你应该看一下System._IntfCopy过程。我已经更新了我的AnonCopy过程,以便在Dest <> nil的情况下递减“Dest”引用计数器。 - kludg

1

看起来最好的方法是使用泛型在记录中存储正确类型的委托。不需要任何黑科技。

program Project51;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
  TMyProc = reference to procedure(var param: integer);

  TStorage<T> = record
    FDelegate: T;
  end;

var
  a    : TMyProc;
  b    : TMyProc;
  p    : TProc;
  param: integer;
  stg  : TStorage<TMyProc>;

begin
  b := procedure (var param: integer)
    begin
      param := 2*param;
    end;
  stg.FDelegate := b;
  param := 21;
  stg.FDelegate(param);
  Writeln(param);
  Readln;
end.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接