2014-07-26 35 views
1

編輯,澄清這個問題,因爲我簡化太多,從而消除了我實際上正面臨爲什麼我不能在方法體之外聲明一個通用的匿名方法?

這個問題我有在長身體的實現委託。
因此,我不想在我使用它的函數中聲明它。

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; 
    ..... 

類型參數也起到提醒的通用程序的實現者是什麼輸入和輸出類型是。

因爲類型參數在方法頭的其餘部分中沒有重複,所以在聲明函數時必須保留它們。
因此Stefan的答案不起作用。

只是聲明它作爲單位常量(或單位變量)不起作用。
由於其通用簽名,將其聲明爲單位過程也不起作用。
下面的代碼無法編譯:

樣品A

const 
    Test: integer = 0; 

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

E2026 Constant expression expected

//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; 

感覺有點畫蛇添足這種方式來做到這一點。
有沒有辦法避免必須將通用匿名函數包裝在另一個函數中?

回答

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. 

編譯罰款。按預期運行。在德爾福XE2測試。

+1

在單元的初始化部分直接分配它不是一個好主意,因爲這會導致內存泄漏報告。如果你想這樣做,你應該把它放到一個例程中,並在初始化部分調用它。 –

+0

@StefanGlienke有很多原因,這不是一個好主意。我只是用它來證明需要在運行時創建匿名方法。另外,將它放入常規會使我們回到使用Johan的'ListSplitterDelegate'的(更好的)解決方案的複雜程度,所以根本沒有理由使用上述方法。 –

2

更新到編輯的問題:

聲明它作爲正常程序應該只是罰款:

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

是的,對於普通功能它可以,但不能用帶有通用點綴的方法。該函數看起來像這樣:'procedure Whatever (const Data:IData ); 開始 //做東西 end;'{{這是沒有意義的,在任何情況下都不允許} – Johan

+0

使用你給出的確切過程給出錯誤:'E2035沒有足夠的實際參數',因爲簽名不匹配; '無論'不是'TTaskDelegate',它只是一個簡單的TTaskDelegate。 – Johan

+0

我寫的例程與您聲明的'TTaskDelegate 兼容。 –

相關問題