如何将由另一个函数返回的函数赋值给函数变量?结果而不是生成函数本身

RM.*_*RM. 16 delphi variables function-pointers function anonymous-methods

函数返回匿名函数.我想将结果分配给变量.但是编译器认为我正在尝试分配函数而不是函数的结果.我该如何解决这个问题?

program Project9;

{$APPTYPE CONSOLE}

type
  TMyEvent = reference to function: string;

var
  v1: TMyEvent;

function GetHandler: TMyEvent;
begin
  Result := function: string
            begin
              Result := '';
            end;
end;

begin
  v1 := GetHandler;  // <- Incompatible types: 'TMyEvent' and 'Procedure'
end.
Run Code Online (Sandbox Code Playgroud)

注意:我确实有一个解决方法,但我希望在不引入包装器的情况下解决这个问题:

program Project9;

{$APPTYPE CONSOLE}

type
  TMyEvent = reference to function: string;

  TWrapper = record
    FHandler: TMyEvent;
  end;

var
  v1: TMyEvent;

function GetHandler: TWrapper;
begin
  Result.FHandler := function: string
                     begin
                       Result := '';
                     end;
end;

begin
  v1 := GetHandler.FHandler;  // <- works
Run Code Online (Sandbox Code Playgroud)

编辑:这不是特定于匿名或任何特殊类型的函数:对于任何返回函数的函数来说都是实际的,即使是第一个Delphi到达之前在Turbo Pascal中也是如此.

Agu*_*ert 18

如果您的匿名方法/函数是无框架的,则必须使用();

v1 := GetHandler();
Run Code Online (Sandbox Code Playgroud)

如果没有括号,Delphi将尝试将函数赋值给变量.括号告诉它将函数结果赋给变量.


Dav*_*nan 6

Delphi的函数调用语法与大多数其他语言略有不同.在大多数语言中,为了调用函数,必须()在函数名后使用parens ,通常称为函数调用运算符.如果简单地命名该函数,并且没有提供任何parens,则该表达式将在不调用调用的情况下求值为该函数.

所以,以C++语言为例,

i = foo();
Run Code Online (Sandbox Code Playgroud)

调用函数并存储返回值i.

另一方面,

fn = foo;
Run Code Online (Sandbox Code Playgroud)

将函数的地址存储在函数指针变量中fn.

Delphi与此不同,对于无参数功能,允许您省略parens,但仍然调用该函数.所以在Delphi中,可以编写上面第一行代码

i := foo;
Run Code Online (Sandbox Code Playgroud)

这将调用该函数.

如果函数返回类型是过程类型,方法或匿名方法,那么它会变得有点棘手.

在您的方案中,

v1 := GetHandler;
Run Code Online (Sandbox Code Playgroud)

在编译器的眼中是模棱两可的.因为v1是一个类型是匿名方法的变量,所以当省略parens时,编译器永远不会生成调用.如果它确实生成了一个调用,那么你就无法将一个函数简单地赋值给一个过程类型变量.

因此编译器会切换到您在C++等语言中找到的行为.如果您希望调用该函数,则必须提供parens.要使代码编译和工作,请编写

v1 := GetHandler();
Run Code Online (Sandbox Code Playgroud)

文档详细介绍了该问题.关键摘录如下:

在赋值语句中,左侧​​变量的类型决定了右侧过程或方法指针的解释.


现在,在判断时,我发现表达式的上下文可以确定其解释是相当令人不安的.这一切都源于允许在省略parens时进行函数调用.我宁愿总是使用parens,所以避免上面讨论的歧义.特别地,这将允许表达意义独立于上下文.

为了明白我的意思,我们回到原来的例子.现在让我们更具体地说明所涉及的类型:

type
  TIntFunc = function: Integer;

function foo: Integer;
begin
  Result := 42;
end;

var
  i: Integer;
  fn: TIntFunc;
Run Code Online (Sandbox Code Playgroud)

此时我们可以写:

i := foo;  // i is an integer, so the function is called
fn := foo; // fn is a procedural type variable, so the function is not called
Run Code Online (Sandbox Code Playgroud)

我个人觉得这种情况根本不令人满意.