2014-10-01 33 views
11

Rozważmy następujący:Generics nie metoda rozwiązywania typów prawidłowo

{$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. 

To daje oczekiwane wyjście

foo 
bar 

Załóżmy teraz chcemy przypisać procedurę z listy. Wyliczanie dzieł, jak wyżej. Działa to również:

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

Ale to generuje błąd kompilatora:

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

Która jest podwójnie dziwne ponieważ

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

Więc ... co się dzieje?

Czy to dotyczy również nowszych wersji Delphi? Kusi mnie to QC, jeśli istnieje uzasadnione oczekiwanie, że to powinno zadziałać (co moim zdaniem jest).

Odpowiedz

13

To nie jest błąd kompilatora, ani nie jest to problem związany z używaniem generycznych. Zarówno First, jak i Last są funkcjami, więc kompilator nie może stwierdzić, czy masz zamiar do nich zadzwonić, czy odwołać się do nich. Bądź jednoznaczny i pozwól, aby kompilator wiedział, że masz zamiar wywołać tę funkcję, dostarczając parens.

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

Po raz kolejny zostałeś złapany dzięki decyzji o pominięciu paren przy uruchamianiu procedur i funkcji. Ta decyzja projektowa, choć wyglądała tak atrakcyjnie, gdy została wykonana, wygląda teraz mniej, gdy typy proceduralne są tak szeroko stosowane w nowoczesnych stylach kodowania.

Podczas pisania ProcList.First kompilator napotyka niejednoznaczność. Czy chcesz wywołać funkcję, czy też chcesz odwołać się do funkcji jako typu proceduralnego? W wielu scenariuszach kompilator nie może rozwiązać niejednoznaczności, ale tak nie jest w tym przypadku, gdy wyrażenie znajduje się po prawej stronie operatora przypisania. W obliczu tej niejednoznaczności kompilator zakłada, że ​​chodzi o odwołanie się do funkcji.

Dokonuje tego wyboru, ponieważ inny wybór byłby gorszy. Przynajmniej w ten sposób możesz dostarczyć parens i wyraźnie wskazać, że masz na myśli wywołanie funkcji. Gdyby kompilator podążał w drugą stronę, wtedy szukałbyś sposobu, aby powiedzieć mu, że chcesz odwołać się do funkcji.

Ostatecznie, jeśli First i Last zostały zaimplementowane jako właściwości, nie byłoby żadnej niejednoznaczności.

+2

Z C#, C, C++, Python i każdego innego języka. Oprócz VB. Zawsze jest źle, gdy znajdziesz się w zestawie o rozmiarze drugim, a innym członkiem jest VB! –

+0

AFAIK, Swift, a więc staje się zestawem trzech. –