为什么我不能在方法体外声明一个通用的匿名方法?

时间:2014-07-26 22:00:01

标签: delphi generics anonymous-function

编辑,澄清了这个问题,因为我将其简化了太多,从而消除了我实际面临的问题

我有一位代表,在机构中实施了很长时间 因此我不想在我正在使用它的函数中声明它。

type
  TTaskDelegate<A, B> = reference to procedure(const Data: IData);
  //-----------^^^^^^ note the type parameters here
  //-But no type parameters here---------------------------^^^^^^

委托被声明为,以便我可以将它存储在看起来像的记录中:

TMultiDelegate = record
  strict  private
    fAA: TTaskDelegate<TOmniValue, TOmniValue>;
    fAB: TTaskDelegate<TOmniValue, IOmniBlockingCollection>;
    fBA: TTaskDelegate<IOmniBlockingCollection, TOmniValue>;
    fBB: TTaskDelegate<IOmniBlockingCollection, IOmniBlockingCollection>;
    fSimple: TSimpleTaskDelegate;
    fOutputCount: Integer;
    function GetDelegateType: TDelegateType;
  public
    constructor Init(AA: TTaskDelegate<TOmniValue, TOmniValue>; const OutputCount: integer = 1); overload;
    constructor Init(AB: TTaskDelegate<TOmniValue, IOmniBlockingCollection>;          const OutputCount: integer = 1); overload;
    .....

类型参数还可以提醒输入和输出类型的泛型过程的实现者。

因为类型参数不在方法标题的其余部分重复,所以在声明函数时必须保留它们。
出于这个原因,斯特凡的回答不起作用。

将其声明为单位常量(或单位变量)不起作用 由于其通用签名,将其声明为单元程序也不起作用 以下代码无法编译:

示例A

const
  Test: integer = 0;

const
  DoesNotCompile: TTaskDelegate<TOmniValue, TOmniValue> =  
    procedure(const Data: IData)
    begin
      //Do stuff    
    end;
  

E2026预期的常量表达式

 //This variant will not compile either.
 procedure DoStuff<TOmniValue, TOmniValue>(const Data: IData)
 begin
   //DoStuff
 end;

当我将它包装在一个函数中时,它确实有效。

样本B

function ListSplitterDelegate: TTaskDelegate<TOmniValue, TOmniValue>;
begin
  Result:=
    procedure(const Data: IData)
    begin
      //Do stuff    
    end;
end;

以这种方式做这件事感觉有点多余 有没有办法避免将通用匿名函数包装在另一个函数中?

2 个答案:

答案 0 :(得分:2)

已更新至已修改的问题:

将其声明为常规程序应该可以正常工作:

procedure Whatever_TOmniValue_TOmniValue(const Data: IData);
begin
  //Do stuff    
end;

答案 1 :(得分:0)

你想做的事是不可能的。匿名方法是有状态的,引用计数的对象本质上不是常数。匿名方法的赋值产生一个闭包,其状态在其环境变化时不断被检查和修改。它在某些方面与其他编译器管理类型一样,如动态数组,由于某些类似的原因也不能是const

您创建ListSplitterDelegate函数的解决方案可能是您可以做的最好的。否则,您需要将DoesNotCompile声明为变量并在运行时分配它。

var 
  CompilesOk : TTaskDelegate<TOmniValue, TOmniValue>;

  //...

initialization 
  CompilesOk := procedure(const Data: IData<TOmniValue, TOmniValue>)
                begin
                  //Do stuff    
                end;

显然,这有一个问题,CompilesOk可以被覆盖(出于多种原因,这是一个坏主意)。 ListSplitterDelegate是最佳解决方案,如果您需要这是一个匿名方法。我不认为您需要这是一个匿名方法,因为可以将常规方法分配给reference to类型

SSCCE演示(使用您更新的代码示例和签名):

unit Unit1;

interface

type
  IData = interface
  end;
  TOmniValue = record
  end;
  TTaskDelegate<A,B> = reference to procedure(const Data: IData);

  TMultiDelegate = record
  strict  private
    fAA: TTaskDelegate<TOmniValue, TOmniValue>;
  public
    constructor Init(AB: TTaskDelegate<TOmniValue, TOmniValue>);
    procedure DoIt;
  end;

implementation

constructor TMultiDelegate.Init(AB: TTaskDelegate<TOmniValue, TOmniValue>);
begin
  fAA := AB;
end;

procedure TMultiDelegate.DoIt;
var dat: IData;
begin
  fAA(dat);
end;

end.

主要:

program Project1;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  Unit1 in 'Unit1.pas';

procedure DoSomething(const Data : IData);
begin
  writeLn('foo');
end;

var
  tmd : TMultiDelegate;
begin
  tmd.Init(DoSomething);
  tmd.DoIt;  
  ReadLn;
end.

编译好。按预期运行。在Delphi XE2中测试。