2010-12-11 3 views
8

Po prostu próbowałem mojego pierwszego użycia generycznych w Delphi 2009 i jestem zakłopotany, w jaki sposób użyć typu ogólnego jako wejścia do funkcji Supports używanej do sprawdzenia, czy obiekt implementuje dane berło. Stworzyłem małą próbkę ilustrującą problem.Używanie funkcji Support() z typowym interfejsem typu

Biorąc pod uwagę następujące rodzaje i użytkową funkcję:

IMyInterface = interface 
['{60F37191-5B95-45BC-8C14-76633826889E}'] 
end; 

TMyObject = class(TInterfacedObject, IMyInterface) 
end; 

class function TFunctions.GetInterface<T>(myObject: TObject): T; 
var 
    specificInterface: T; 
begin 
    // This would compile, but looses the generic capability 
    //Supports(myObject, IMyInterface, specificInterface); 

    // This results in compile errors 
    Supports(myObject, T, specificInterface); 

    result := specificInterface; 
end; 

i następujący fragment kodu:

class procedure TFunctions.Test; 
var 
    myObject: TMyObject; 
    myInterface: IMyInterface; 
begin 
    myObject := TMyObject.Create; 

    myInterface := GetInterface<IMyInterface>(myObject); 
end; 

spodziewałbym żadnych problemów, ale pojawia się następujące błędy kompilacji czas:

[Błąd DCC] GenericExample.pas (37): E2029 '(' oczekiwano, ale ',' znaleziono [Błąd DCC] Generuj icExample.pas (37): E2014 komunikat z oczekiwaniami, ale wyrażenie typu „T” Znaleziono

Nie jestem pewien, co kompilator spodziewa mi zrobić z T stosowany jako rzeczywisty argument funkcji .

Szukałem sporo i nie byłem w stanie go złamać. Część mnie podejrzewa, że ​​gdybym mógł zrozumieć, w jaki sposób nazwa interfejsu zostanie przekształcona w IID: typ TGUID podczas kompilacji, używając konkretnej nazwy interfejsu, mógłbym zrobić pewne postępy, ale to mnie również omija.

Każda pomoc jest doceniana.

Odpowiedz

7

Nie ma gwarancji, że T ma przypisany identyfikator GUID, aw języku nie ma środków, aby napisać ograniczenie dotyczące parametru typu, aby uzyskać tę gwarancję.

Nazwa interfejsu jest przekształcana na identyfikator GUID przez kompilator szukający nazwy w tabeli symboli, pobierający strukturę danych kompilatora reprezentującą interfejs i sprawdzający odpowiednie pole dla identyfikatora GUID. Ale generics nie są podobne do szablonów C++; muszą być kompilowane i sprawdzane i sprawdzane pod kątem dowolnego poprawnego parametru typu, co oznacza ograniczenie parametru typu w deklaracji.

Możesz uzyskać identyfikator GUID za pomocą RTTI (najpierw sprawdzając, czy T rzeczywiście reprezentuje interfejs) z czymś takim, jak GetTypeData(TypeInfo(T))^.Guid i przekazując identyfikator GUID do Supports w ten sposób.

+0

Nie można zastosować ograniczenia, że ​​T musi być określonym interfejsem z identyfikatorem GUID? –

+1

Jeśli musi to być jakiś interfejs, kod nie jest generyczny. Obsługa zwróci odniesienie do interfejsu; kompilator nie może generalnie zmienić tego na dowolne T. –

+1

Barry, dzięki za pomoc. Większość mojego doświadczenia z podstawowym programowaniem pochodzi z języka C++ i spodziewałem się, że kompilator będzie wiedział, czy interfejs ma Guid w taki sam sposób, w jaki będzie wiedział, czy nazwa interfejsu została podana bezpośrednio. Ma to sens teraz wiedząc, że generyczne szablony <> C++. Dzięki jeszcze raz. – Chad

3

Dlaczego się martwisz?

Aby skorzystać z tej TFunctions.GetInterface trzeba:

  • interfejs
  • odwołanie do obiektu

Jeśli masz te, to można po prostu zadzwonić nośnych() bezpośrednio:

intf := TFunctions.GetInterface<IMyInterface>(myObject); 

to dokładnie equivale do:

Supports(IMyInterface, myObject, intf); 

Użycie generycznych tutaj to strata czasu i wysiłku i naprawdę błaga o pytanie "Dlaczego to zrobić?".

Po prostu utrudnia czytanie (jak to często bywa w przypadku leków generycznych) i jest bardziej niewygodne w użyciu.

Podpory() zwraca dogodnym logiczną do wskazania sukcesu/porażki, które trzeba przetestować oddzielnie przy użyciu opakowanie:

intf := TFunctions.GetInterface<IMyInterface>(myObject); 
    if Assigned(intf) then 
    // ... 

versus:

if Supports(IMyInterface, myObject, intf) then 
    // We can use intf 

Tworząc obwoluty wokół Funkcjonalność na ogół skutkuje poprawą czytelności lub użyteczności.

imho to się nie uda z obu powodów i powinieneś po prostu trzymać się funkcji Supports().

+0

Mój przykład jest destylowany z rzeczywistego kontekstu, aby zadać konkretne pytanie i nie jest ilustracją rzeczywistego projektu. – Chad

+2

Następnie musisz podać co najmniej NIEKTÓRE z kontekstu. W przeciwnym razie jest to jak pytanie, jak dostać się z punktu A do punktu B, nie wspominając o tym, że nie możesz prowadzić samochodu i nie masz pieniędzy na bilet autobusowy. Możliwe, że nawet w kontekście, to, co robisz, nie jest najlepszym/najłatwiejszym sposobem radzenia sobie z tym. Wszystko jest dobrze i dobrze, zadając konkretne pytanie, ale jeśli chcesz uzyskać pomocne odpowiedzi, to nie jest dobrze wyrzucać ważny kontekst. – Deltics

+2

Z całym szacunkiem się nie zgadzam. Twoje zdrowie. – Chad