Comment affecter une fonction, retournée par une autre fonction, à une variable de fonction? Le résultat plutôt que la fonction génératrice elle-même

Une fonction retourne une fonction anonyme. Je voudrais affecter le résultat à une variable. Cependant le compilateur pense que j'essaie d'attribuer la fonction et non le résultat de la fonction. Comment puis-je résoudre ce problème?

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.

Note: j'ai une solution mais j'espère que ce problème ne peut être résolu sans l'introduction d'un wrapper:

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: ceci n'est pas spécifique à anonymous ou à tout type particulier de fonctions: cela est réel pour tout fonction retournant la fonction, il était le même dans Turbo Pascal avant même le 1er Delphi est arrivé.

15
demandé sur Arioch 'The 2014-01-21 03:05:31

2 réponses

si vos méthodes/fonctions anonymes sont paramless, vous devez assigner avec ();

v1 := GetHandler();

Sans les crochets Delphi va essayer d'attribuer le function à la variable. Les crochets lui disent d'attribuer le résultat de la fonction à la variable.

18
répondu Agustin Seifert 2014-01-20 23:46:56

la syntaxe des appels de fonction de Delphi est un peu différente de la plupart des autres langues. Dans la plupart des langues, afin d'appeler une fonction, vous devez utiliser des parenthèses () après le nom de la fonction, communément appelé opérateur d'appel de fonction. Si la fonction est simplement nommée, et aucun parens fourni, cette expression est évaluée à la fonction sans invoquer un appel.

donc, avec le langage C++ comme exemple,

i = foo();

appelle la fonction et stocke l' la valeur dans i.

d'autre part,

fn = foo;

stocke l'adresse de la fonction dans la fonction pointeur de variable fn.

Delphi varie, pour une fonction sans paramètre, en vous permettant d'omettre les parenthèses, et pourtant toujours appeler la fonction. Ainsi, dans Delphi, la première ligne de code ci-dessus pourrait être écrite

i := foo;

et cela appellerait la fonction.

où il devient légèrement délicat est si la fonction le type de retour est un type procédure, une méthode ou une méthode anonyme, comme vous l'avez trouvé.

Dans votre scénario,

v1 := GetHandler;

est ambiguë aux yeux du compilateur. Parce que v1 est une variable dont le type est une méthode anonyme, le compilateur ne générera jamais d'appel lorsque les parens sont omis. S'il générait un appel alors vous ne seriez pas en mesure de faire la simple attribution d'une fonction à une variable de type procédural.

Donc le compilateur passe au comportement que vous trouvez dans les langages comme C++. Vous devez fournir parens si vous souhaitez que la fonction soit appelée. Pour que votre code soit compilé et fonctionne, écrivez

v1 := GetHandler();

documentation couvre la question en détail. La extrait la clé est ceci:

dans les instructions d'affectation, le type de la variable à gauche détermine l'interprétation des pointeurs de procédure ou de méthode sur le droit.


Maintenant, la coulée de jugement, je trouve l'idée que le contexte d'une expression peut déterminer son interprétation plutôt troublant. Tout cela provient du fait que les appels de fonction peuvent être faits lorsque les parens sont omis. Je préférerais utiliser le parens toujours et donc éviter les ambiguïtés discutées ci-dessus. En particulier, cela permettrait à la signification de l'expression d'être indépendante du contexte.

pour voir ce que je veux dire, nous revenons à mon original exemple. Soyons plus précis sur les types impliqués:

type
  TIntFunc = function: Integer;

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

var
  i: Integer;
  fn: TIntFunc;

À ce stade, nous pouvons écrire:

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

je trouve personnellement cet état de fait pas du tout satisfaisant.

6
répondu David Heffernan 2014-01-21 09:14:56