Jak rozwiązać problem z błędem „Niezgodne typy E2010:„ TGUID ”i„ T ””?
Jest to dla mnie nieco zastanawiające, ponieważ pracuję nad jednostką z kilkudziesięcioma interfejsami, które są oparte na tej podstawowej definicji interfejsu:
type
IDataObject = interface(IInterface)
['{B1B3A532-0E7D-4D4A-8BDC-FD652BFC96B9}']
function This: TDataObject;
end;
ISomeObject = interface(IDataObject)
['{7FFA91DE-EF15-4220-A43F-2C53CBF1077D}']
<Blah>
end;
Oznacza to, że wszystkie mają metodę „This”, która zwraca klasę znajdującą się za interfejsem, co czasami jest potrzebne do umieszczenia widoków list i innych rzeczy, ale w przypadku tego pytania nie ma to większego znaczenia, ponieważ potrzebuję klasy ogólnej z dodatkowymi funkcjami, które mogą być stosowane do dowolnego interfejsu pochodnego. (Każdy interfejs pochodny ma swój własny identyfikator GUID). Oto klasa ogólna:
type
Cast<T: IDataObject> = class(TDataObject)
class function Has(Data: IDataObject): Boolean;
class function Get(Data: IDataObject): T;
end;
Nie wygląda na zbyt skomplikowane, a użycie metod klasowych jest spowodowane tym, że Delphi nie obsługuje globalnych funkcji ogólnych, chyba że znajdują się one w klasie. Dlatego w moim kodzie chcę Cast<ISomeObject>.Has(SomeObject)
sprawdzić, czy obiekty obsługują określony interfejs. Get()
Funkcja jest po prostu zwrócić przedmiot szczególnego rodzaju, jeśli to możliwe. A więc dalej wdrożenie:
class function Cast<T>.Get(Data: IDataObject): T;
begin
if (Data.QueryInterface(T, Result) <> S_OK) then
Result := nil;
end;
class function Cast<T>.Has(Data: IDataObject): Boolean;
begin
Result := (Data.QueryInterface(T, Result) = S_OK);
end;
I tutaj robi się irytujące! W innym miejscu mojego kodu używam if (Source.QueryInterface(ISomeObject, SomeObject) = 0) then ...
i działa dobrze. W tych ogólnych metodach ISomeObject
jest zastępowany przez T
i powinien po prostu działać. Ale odmawia kompilacji i podaje ten błąd:
[Błąd dcc64] DataInterfaces.pas (684): E2010 Niezgodne typy: „TGUID” i „T”
I to jest denerwujące. Muszę to naprawić, ale nie mogę znaleźć odpowiedniego rozwiązania bez włamywania się głęboko do kodu interfejsu jednostki systemowej. (Która jest jedyną jednostką, której mogę używać w tym kodzie, ponieważ musi działać na wielu różnych platformach!)
Błąd jest poprawny, ponieważ QueryInterface oczekuje TGUID jako parametru, ale wydaje się, że pobiera go z ISomeObject. Więc dlaczego nie od T?
Myślę, że próbuję tutaj dokonać niemożliwego ...
Mówiąc bardziej konkretnie: Source.QueryInterface(ISomeObject, SomeObject)
działa dobrze bez użycia innej jednostki. Spodziewałbym się więc, że będzie działać z typem ogólnym, jeśli ten typ jest ograniczony do interfejsów. Ale tak nie jest i chcę wiedzieć, dlaczego nie akceptuje T, podczas gdy akceptuje ISomeObject.
Czy możesz wyjaśnić, dlaczego nie działa z typem ogólnym, a nie zwykłym typem interfejsu?
Odpowiedzi
QueryInterface()
przyjmuje TGUID
jako dane wejściowe, ale typ interfejsu nie jest TGUID
. Kompilator ma specjalną obsługę podczas przypisywania typu interfejsu z zadeklarowanym identyfikatorem GUID do TGUID
zmiennej, ale wydaje się, że nie ma to zastosowania wewnątrz parametru Generic, który używa ograniczenia interfejsu. Tak więc, aby zrobić to, co próbujesz, będziesz musiał po prostu przeczytać RTTI interfejsu w czasie wykonywania, aby wyodrębnić jego rzeczywisty TGUID
(zobacz Czy można uzyskać wartość identyfikatora GUID na interfejsie przy użyciu RTTI? ), Np .:
uses
..., TypInfo;
class function Cast<T>.Get(Data: IDataObject): T;
var
IntfIID: TGUID;
begin
IntfIID := GetTypeData(TypeInfo(T))^.GUID;
if (Data.QueryInterface(IntfIID, Result) <> S_OK) then
Result := nil;
end;
class function Cast<T>.Has(Data: IDataObject): Boolean;
begin
Cast<T>.Get(Data) <> nil;
end;
Biorąc to pod uwagę, dlaczego duplikujesz funkcjonalność, którą RTL już zapewnia natywnie?
Cała twoja Cast
klasa jest niepotrzebna, po prostu użyj SysUtils.Supports()zamiast tego ( SysUtils
jednostka jest wieloplatformowa), np:
uses
..., SysUtils;
//if Cast<ISomeObject>.Has(SomeObject) then
if Supports(SomeObject, ISomeObject) then
begin
...
end;
...
var
Intf: ISomeObject;
//Intf := Cast<ISomeObject>.Get(SomeObject);
if Supports(SomeObject, ISomeObject, Intf) then
begin
...
end;
Ponadto twoja IDataObject.This
właściwość jest zupełnie niepotrzebna, ponieważ możesz bezpośrednio rzutować IDataObject
interfejs na obiekt jej TDataObject
implementacji (Delphi obsługuje takie rzutowanie od D2010), np .:
var
Intf: IDataObject;
Obj: TDataObject;
Intf := ...;
Obj := TDataObject(Intf);