具有不同签名的功能指针(示例:带有默认值的可选参数)

时间:2013-11-06 08:43:13

标签: delphi function-pointers signature

是否可以使用默认参数创建一个函数指针,如

TFunctionPointer = function(sName:AnsiString; tOptional: TObject = nil):smallint;

我想要实现的目标:

一个函数指针,可以接受类型

的函数
function A(sName:AnsiString)

function B(sName:AnsiString, tOptional: TObject)

我怎样才能做到这一点?

2 个答案:

答案 0 :(得分:7)

默认参数只是一个语法糖 - 实际上函数调用有两个参数。

但是你可以使用函数引用和匿名方法来创建这样的函数指针 - 函数适配器。

type
  fnA = function(const sName: AnsiString): integer;
  fnB = function(const sName: AnsiString; const tOptional: TObject); integer;

  fnRef = reference to function(const sName: AnsiString; const tOptional: TObject): integer;

  fnBridge = record 
     Bridge: fnRef;
     class operator Implicit(fn: fnA): fnBridge;
     class operator Implicit(fn: fnB): fnBridge;
  end;

class operator fnBridge.Implicit(fn: fnA): fnBridge;
begin
  Result.Bridge := 
     function(const sName: AnsiString; const tOptional: TObject): integer
     begin
       Result := fn(sName); 
     end;
end;

class operator fnBridge.Implicit(fn: fnB): fnBridge;
begin
  Result.Bridge := 
     function(const sName: AnsiString; const tOptional: TObject): integer
     begin
       Result := fn(sName, tOptional); 
     end;
end;

function A(const sName: AnsiString): integer; 
begin Result := Length(sName) end;

function B(const sName: AnsiString; const tOptional: TObject): integer; 
begin Result := Length(sName) - Length(tOptional.ClassName) end;

function Consumer (const Param1, Param2: integer; const Action: fnBridge): integer;
begin
  Result := Param1 + Param2 * Action.Bridge('ABCDE', Application);
end;

....
  ShowMessage( IntToStr( Consumer(10, 20, A) ));
  ShowMessage( IntToStr( Consumer(10, 20, B) ));

PS:由于未指定Delphi版本,这意味着任何Delphi版本的答案都适合。从Delphi 2009及更高版本开始,这个方法应该是hany版本。

PPS:对具有捕获变量的函数的引用在内部实现为TInterfacedObject后代。总的来说,这只是使用“高阶函数”的“战略模式”的简化案例

答案 1 :(得分:3)

这是不可能的。为了使函数为TFunctionPointer类型,它必须声明两个参数。

默认参数仍然是参数。您的TFunctionPointer是一个包含两个参数的函数。当您调用它并仅提供一个参数时,编译器会在调用站点提供默认参数。所以两个参数仍然传递给函数。

要扩展这一点。请考虑以下事项:

procedure Foo(Bar: Integer=666);
begin
end;

当你调用这样的程序时:

Foo();

看起来好像程序没有参数。但事实并非如此。编译器将您的代码转换为:

Foo(666);

结论是,如果要允许接收具有不同数量参数的函数,则需要提供一种显式机制来接收这些不同的函数类型。例如:

procedure DoSomething(const Callback: TProc<string, TObject>); overload;
begin
  Callback(str, obj);
end;

procedure DoSomething(const Callback: TProc<string>); overload;
begin
  DoSomething(
    procedure(arg1: string; arg2: TObject)
    begin
      Callback(arg1);
    end
  );
end;