2014-10-01 9 views
11

Si consideri il seguente:Generics non risolvere i tipi di metodo correttamente

{$APPTYPE CONSOLE} 

uses 
    Generics.Collections; 

type 
    TObjProc = procedure of object; 
    TFoo = class 
    public procedure DoFoo; 
    public procedure DoBar; 
    end; 

procedure TFoo.DoFoo; 
begin 
    WriteLn('foo'); 
end; 

procedure TFoo.DoBar; 
begin 
    WriteLn('bar'); 
end; 

var 
    ProcList : TList<TObjProc>; 
    Foo : TFoo; 
    aProc : TObjProc; 
begin 
    Foo := TFoo.Create; 
    ProcList := TList<TObjProc>.Create; 
    ProcList.Add(Foo.DoFoo); 
    ProcList.Add(Foo.DoBar); 
    for aProc in ProcList do aProc; 
    ReadLn; 
end. 

Questo produce il risultato atteso di

foo 
bar 

Ora supponiamo di voler assegnare un procedimento dalla lista. Enumerazione dei lavori, come sopra. Questo funziona anche:

aProc := ProcList.Items[0]; 
aProc; 

Ma questo getta un errore di compilazione:

aProc := ProcList.First; 
// E2010 Incompatible types: 
//'procedure, untyped pointer or untyped parameter' and 'TObjProc' 

Quale è doppiamente strano dato

function TList<T>.First: T; 
begin 
    Result := Items[0]; 
end; 

Allora ... cosa sta succedendo?

Questo influisce anche sulle versioni più recenti di Delphi? Sono tentato di QC questo se c'è una ragionevole aspettativa che questo dovrebbe funzionare (che credo ci sia).

risposta

13

Questo non è un bug del compilatore, né in effetti questo problema è correlato all'uso di generici. Entrambe le funzioni First e Last sono funzioni in modo che il compilatore non possa stabilire se si intende chiamarle o farvi riferimento. Sii esplicito e fai sapere al compilatore che intendi chiamare la funzione fornendo parenti.

aProc := ProcList.First(); 
aProc := ProcList.Last(); 

Ancora una volta sei stato catturato dalla decisione di consentire l'omissione dei parenti quando si invocano procedure e funzioni. Questa decisione progettuale, pur sembrando così attraente quando è stata realizzata, sembra meno ora che i tipi procedurali sono così ampiamente utilizzati negli stili di codifica moderni.

Quando si scrive ProcList.First il compilatore affronta un'ambiguità. Intendi chiamare la funzione o desideri fare riferimento alla funzione come un tipo procedurale? In molti scenari, il compilatore non può risolvere l'ambiguità, ma non è il caso qui, dove l'espressione si trova sul lato destro di un operatore di assegnazione. Di fronte a questa ambiguità il compilatore presume che tu intenda riferirsi alla funzione.

Prende questa scelta perché l'altra scelta sarebbe peggio. Almeno in questo modo è possibile fornire i parenti e indicare esplicitamente che si intende per la funzione da chiamare. Se il compilatore fosse andato dall'altra parte, verrebbe comunque lasciato alla ricerca di un modo per dire che intendevi fare riferimento alla funzione.

Infine, se e Last fossero state implementate come proprietà, non ci sarebbe stata alcuna ambiguità.

+2

Da C#, C, C++, Python e ogni altra lingua. Oltre a VB. È sempre brutto quando ti trovi in ​​un set di taglia due e l'altro membro è VB !! –

+0

E, AFAIK, Swift, quindi diventa un set di tre. –

Problemi correlati