Reputation: 76567
Edit, clarified the question, because I simplified it too much, thereby eliminating the problem I was actually facing
I have a delegate with a long implementation in the body.
For that reason I don't want to declare it inside the function where I'm using it.
type
TTaskDelegate<A, B> = reference to procedure(const Data: IData);
//-----------^^^^^^ note the type parameters here
//-But no type parameters here---------------------------^^^^^^
The delegate is declared like the so that I can store it in a record that looks like:
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;
.....
The type parameters also serve as a reminder to the implementor of the generic procedure what the input and output types are.
Because the type parameters are not repeated in the rest of the method header, they have to be retained when declaring the function.
For that reason Stefan's answer does not work.
Just declaring it as a unit constant (or unit variable) does not work.
Declaring it as a unit procedure will also not work, because of its generic signature.
The following code does not compile:
Sample 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;
When I wrap it in a function it does work.
Sample B
function ListSplitterDelegate: TTaskDelegate<TOmniValue, TOmniValue>;
begin
Result:=
procedure(const Data: IData)
begin
//Do stuff
end;
end;
It feels a bit superfluous to do it this way.
Is there a way to avoid having to wrap the generic anonymous function inside another function?
Upvotes: 1
Views: 1130
Reputation: 31403
What you are trying to do is not possible. Anonymous methods are stateful, reference counted objects that are not constant by nature. Assignment of an anonymous method produces a closure whose state is continually inspected and modified as it its environment changes. It is in some ways like other compiler managed types, like dynamic arrays, which for somewhat similar reasons also cannot be a const
.
Your solution of creating the ListSplitterDelegate
function is probably the best you can do. Otherwise, you would need to declare DoesNotCompile
as a variable and assign it at runtime.
var
CompilesOk : TTaskDelegate<TOmniValue, TOmniValue>;
//...
initialization
CompilesOk := procedure(const Data: IData<TOmniValue, TOmniValue>)
begin
//Do stuff
end;
Obviously, this has the problem that CompilesOk
can be overwritten (and is otherwise a bad idea for a number of reasons). The ListSplitterDelegate
is the best solution if you need this to be an anonymous method. I don't think you need this to be an anonymous method, however, since it is possible to assign a regular method to a reference to
type.
SSCCE to demonstrate (using your updated code example and signatures):
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.
Main :
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.
Compiles fine. Operates as expected. Tested in Delphi XE2.
Upvotes: 0
Reputation: 21713
Updated to the edited question:
Declaring it as regular procedure should work just fine:
procedure Whatever_TOmniValue_TOmniValue(const Data: IData);
begin
//Do stuff
end;
Upvotes: 2