Как назначить функцию, возвращаемую другой функцией, переменной функции? Результат, а не сама производящая функция


Функция возвращает анонимную функцию. Я хотел бы присвоить результат переменной. Однако компилятор думает, что я пытаюсь присвоить функцию, а не результат функции. Как я могу решить эту проблему?

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.

Примечание: у меня есть обходной путь, но я надеюсь, что эта проблема может быть решена без введения оболочки:

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

EDIT: это не относится к анонимным или каким-либо особым функциям: это актуально для любой функции, возвращающей функция, это было то же самое в Turbo Pascal, прежде чем даже 1-й Delphi прибыл.

2 15

2 ответа:

Если ваши анонимные методы / функции не имеют параметров, вы должны назначить с помощью ();

v1 := GetHandler();

Без скобок Delphi попытается присвоить переменной функцию . Скобки говорят ему назначить переменной результат функции .

Синтаксис вызова функций Delphi немного отличается от большинства других языков. В большинстве языков для вызова функции необходимо использовать parens () после имени функции, обычно называемого оператором вызова функции. Если функция имеет простое имя и не содержит паренов, то это выражение вычисляется для функции без вызова вызова.

Итак, с языком C++ в качестве нашего примера,

i = foo();

Вызывает функцию и сохраняет возвращаемое значение в i.

С другой стороны,

fn = foo;

Сохраняет адрес функции в переменной указателя функции fn.

Delphi отличается от этого, для функции без параметров, позволяя вам опустить parens, и все же вызвать функцию. Таким образом, в Delphi первая строка кода выше может быть записана
i := foo;

И это вызовет функцию.

Где это становится немного сложнее, если возвращаемый функцией тип является процедурным типом, методом или анонимный метод, как вы уже выяснили.

В вашем сценарии,

v1 := GetHandler;

Неоднозначен в глазах компилятора. Поскольку v1 является переменной, тип которой является анонимным методом, компилятор никогда не будет генерировать вызов, когда parens опущены. Если бы он действительно генерировал вызов, то вы не смогли бы выполнить простое назначение функции переменной процедурного типа.

Таким образом, компилятор переключается на поведение, которое вы найдете в таких языках, как C++. Вы должны поставлять parens, если вы хотите, чтобы функция была вызвана. Чтобы ваш код компилировался и работал, напишите

v1 := GetHandler();

Документация охватывает этот вопрос довольно подробно. Ключевой отрывок таков:

В операторах присваивания тип переменной слева определяет интерпретацию указателей процедур или методов справа.


Теперь, вынося суждение, я нахожу идею о том, что контекст выражения может определять его интерпретацию, чтобы это было бы довольно тревожно. Все это происходит из-за разрешения вызовов функций, когда парены опущены. Я бы предпочел всегда использовать parens и таким образом избегать двусмысленностей, обсужденных выше. В частности, это позволило бы смыслу выражения быть независимым от контекста. Чтобы понять, что я имею в виду, вернемся к моему первоначальному примеру. Давайте теперь более конкретно о типах, участвующих в этом:
type
  TIntFunc = function: Integer;

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

var
  i: Integer;
  fn: TIntFunc;

В этот момент мы можем написать:

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

Я лично нахожу такое положение дел совсем не удовлетворительно.