2010-06-02 14 views
6

Przechowuję małe interfejsy z szeregu obiektów w jednym sklepie "TInterfaceList" z zamiarem zaoferowania listy konkretnych typów interfejsu dla użytkownika końcowego, tak aby interfejs wyświetli funkcję "GetName", ale wszystkie inne metody są unikalne dla tego typu interfejsu. Na przykład tutaj są dwa interfejsy:Jak przechowywać i lokalizować wiele typów interfejsów w Delphi TInterfaceList

IBase = interface 
    //---------------------------------------- 
    function GetName : string; 
    //---------------------------------------- 
    end; 

    IMeasureTemperature = interface(IBase) 
    //------------------------------------ 
    function MeasureTemperature : double; 
    //---------------------------------------- 
    end; 

    IMeasureHumidity = interface(IBase) 
    //---------------------------------------- 
    function MeasureHumidity: double; 
    //---------------------------------------- 
    end; 

umieścić kilka z tych interfejsów w jednym TInterfaceList a następnie chciałbym zeskanować listę dla określonego typu interfejsu (np „IMeasureTemperature”) buduje kolejną listę wskaźników do obiektów eksportujących te interfejsy. Nie chcę nic przypuszczać o lokalizacji tych obiektów, niektóre mogą eksportować więcej niż jeden typ interfejsu. Wiem, że mogę to zrobić z wykorzystaniem hierarchii klasowej coś takiego:

If FList[I] is TMeasureTemperature then .. 

ale chciałbym coś zrobić simliar z typem interfejsu, jest to możliwe?

+1

Możesz chcieć wyjaśnić, czy twoja pierwsza lista zawiera rzeczywisty typ * interfejsu * lub odniesienia do obiektu * instancji *, które implementują te interfejsy. Wygląda na to, że wszyscy ludzie, którzy tu odpowiedzieli, założyli to drugie, a ja przeczytałem twoje pytanie, aby porozmawiać o tym pierwszym ... potem znowu nie możesz przechowywać * typów interfejsów * w "TInterfaceList" ... więc prawdopodobnie jestem w błędzie ... –

Odpowiedz

2

Sądzę, że to może zaspokoić Twoje potrzeby.

function InterfaceRefIsInterface(Intf : IUnknown; ExpectedIntf : TGUID) : Boolean; 
var vReference : IUnknown; 
begin 
    if Supports(Intf, ExpectedIntf, vReference) then 
    Result := Intf = vReference 
    else 
    Result := False; 
end; 

Nie jestem pewien, jak funkcja będzie zachowywać się podczas INTF i ExpectedIntf dziedziczy się od siebie, ale to wróci TRUE w przypadku INTF jest dokładne dopasowanie ExpectedIntf.

Na przykład exepleHumidity nie zwróci wartości true w IMeasureTemperature, ale nie jestem pewien, jak zareaguje na IBase. Według wstępnych testów zwróci również FALSE na IBase.

+0

Z wyjątkiem, gdy 'ExpectedIntf' to' IUnknown', nie ma gwarancji, że 'Intf' będzie kiedykolwiek równe 'vReference'. [Zasady implementacji QueryInterface] (http://msdn.microsoft.com/en-us/library/ms686590.aspx) obiecują, że wywołanie QueryInterface powiedzie się, ale nie wymagają zwracania żadnego konkretnego wskaźnika interfejsu. –

+0

Z własnego łącza "wywołanie QueryInterface z IID_IUnknown musi zawsze zwracać tę samą wartość wskaźnika fizycznego" i "Każdy interfejs musi mieć możliwość zapytania o dowolny inny interfejs obiektu". Nie jestem pewien, którą regułę polecisz, aby to się nie udało. –

+0

To działa dobrze i robi dokładnie to, na co liczyłem, chociaż musiałbym bardzo uważnie przyjrzeć się temu, co robi! Nigdy nie będzie przypadku, w którym dziedziczony interfejs IBAS zostanie przekazany. Wielkie dzięki. –

6

Wystarczy użyć podpory, tak:

var 
    oMTIntf: IMeasureTemperature; 
... 
    If Supports(FList[I], IMeasureTemperature, oMTIntf) then .. 
+0

@ da-soft: Dobra sugestia, ale zawiedzie, gdy jedna klasa eksportuje dwa różne interfejsy - "Support" zwraca true w tym przypadku dla każdego. Potrzebuję jakiegoś sposobu identyfikacji rzeczywistego typu interfejsu. –

+0

Przepraszam, wydaje mi się, że przegapiłem twój kluczowy punkt. Czy chcesz pobrać obiekt z interfejsu lub uzyskać interfejs z interfejsu? Jeśli pierwszy, to http://www.malcolmgroves.com/blog/?p=500. Jeśli drugi, to jak napisałem (zredagowałem moją wiadomość). –

+0

@Brian: Dlaczego to jest problem? Jeśli jeden z dwóch interfejsów pochodzi od drugiego, sprawdź klasę potomków przed klasą nadrzędną. A jeśli nie, oba wyniki są równie ważne. –

2

może korzystać z funkcji w sysutils Supports, są one dość bezpieczne (chyba spróbować je na pamięć unitialized) i trzeba tylko zmienną docelową dokładna typ interfejsu, który próbujesz przesłać:


procedure DoSomethingInList(AList: IInterfaceList;); 
var 
    i: Integer; 
    liItem: IInterface; 
    liMeasureTemp: IMeasureTemperature; 
    liMeasureHumi: IMeasureHumidity; 
begin 
    AList.Lock; 
    try 
    for i := 0 to AList.Count - 1 do 
    begin 
     liItem := AList[i]; 
     if Supports(liItem, IMeasureTemperature, liMeasureTemp) then 
     //... liMeasureTemp.MeasureTemperature ... 
     else if Supports(liItem, IMeasureHumidity, liMeasureHumi) then 
     //... liMeasureHumi.MeasureHumidity ... 
     else 
     //... 
    end; 
    finally 
    AList.Unlock; 
    end; 
end; 
Powiązane problemy