Как назначить функцию, возвращенную другой функцией, переменной функции? Результат, а не сама производящая функция
Функция возвращает анонимную функцию. Я хотел бы присвоить результат переменной. Однако компилятор считает, что я пытаюсь назначить функцию, а не результат функции. Как я могу это решить?
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, прежде чем прибыл первый Delphi.
Ответы
Ответ 1
Если ваши анонимные методы/функции являются безлимитными, вы должны назначить с помощью();
v1 := GetHandler();
Без скобок Delphi попытается назначить функцию переменной. Скобки говорят ему, чтобы назначить результат функции переменной.
Ответ 2
Синтаксис вызова функции Delphi немного отличается от большинства других языков. В большинстве языков для вызова функции вы должны использовать parens ()
после имени функции, обычно называемой оператором вызова функции. Если функция просто названа и не переданы парсером, это выражение оценивается функцией без вызова вызова.
Итак, с языком С++ в качестве нашего примера,
i = foo();
вызывает функцию и сохраняет возвращаемое значение в i
.
С другой стороны,
fn = foo;
хранит адрес функции в переменной указателя функции fn
.
Delphi варьируется от этого, для беспараметрической функции, позволяя вам игнорировать парсеры и все же вызывать функцию. Поэтому в Delphi первая строка кода выше может быть записана
i := foo;
и это вызовет функцию.
Там, где это становится немного сложно, если тип возвращаемой функции - это процедурный тип, метод или анонимный метод, как вы узнали.
В вашем сценарии
v1 := GetHandler;
является неоднозначным в глазах компилятора. Поскольку v1
- это переменная, тип которой является анонимным, компилятор никогда не будет генерировать вызов, когда парсеры будут опущены. Если он действительно вызвал вызов, вы не сможете сделать простое назначение функции переменной процедурного типа.
Итак, компилятор переключается на поведение, которое вы найдете на таких языках, как С++. Вы должны поставить 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
Я лично считаю, что такое положение дел не является удовлетворительным.