Reputation: 850
I'm getting a IEnumVariant from a .NET class library and I am trying to use a generic class to convert this to a IEnumerator
There is a compiler error, "Operator not applicable to this operand type" when attempting to cast an IInterface to the generic type T I've seen workarounds when attempting to type cast to a class, but these don't work for an interface.
Using Supports as suggested by Rob seems to have problems as well as TypeInfo returns nil for the parameterized type.
uses WinApi.ActiveX, Generics.Collections;
type
TDotNetEnum<T: IInterface> = class(TInterfacedObject, IEnumerator<T>)
strict private
FDotNetEnum: IEnumVariant;
FCurrent: T;
function MoveNext: Boolean;
procedure Reset;
function GetCurrent: TObject;
function IEnumerator<T>.GetCurrent = GenericGetCurrent;
function GenericGetCurrent: T;
public
constructor Create(const ADotNetObject: OleVariant);
//// I can get it to work using this constructor
// constructor Create(const ADotNetObject: OleVariant; const AGUID: TGUID);
end;
implementation
uses System.Rtti, SysUtils, mscorlib_TLB, ComObj;
constructor TDotNetEnum<T>.Create(const ADotNetObject: OleVariant);
var
netEnum: IEnumerable;
begin
netEnum := IUnknown(ADotNetObject) as mscorlib_TLB.IEnumerable;
FDotNetEnum := netEnum.GetEnumerator();
end;
function TDotNetEnum<T>.GenericGetCurrent: T;
begin
result := FCurrent;
end;
function TDotNetEnum<T>.GetCurrent: TObject;
begin
result := nil;
end;
function TDotNetEnum<T>.MoveNext: Boolean;
var
rgvar: OleVariant;
fetched: Cardinal;
ti: TypeInfo;
guid: TGUID;
begin
OleCheck(FDotNetEnum.Next(1, rgvar, fetched));
result := fetched = 1;
if not result then
FCurrent := nil
else
begin
FCurrent := IUnknown(rgvar) as T; // <-- Compiler error here
//// Doesn't work using Supports either
// ti := TypeInfo(T); // <-- returns nil
// guid := GetTypeData(@ti)^.Guid;
// Supports(IUnknown(rgvar), guid, FCurrent);
end;
end;
procedure TDotNetEnum<T>.Reset;
begin
OleCheck(FDotNetEnum.Reset);
end;
Am I missing something in order to get that case to the generic interface type to work ?
I do have the alternative constructor which I CAN get the guid from so that
TDotNetEnum<IContact>.Create(vContactList, IContact);
works but the ideal
TDotNetEnum<IContact>.Create(vContactList);
doesn't
Upvotes: 3
Views: 1546
Reputation: 163247
Using as
to cast interfaces is only valid for interfaces that have GUIDs. The compiler cannot assume that T
has a GUID when it's compiling your generic class, so it cannot accept an expression of the form val as T
.
This has been covered before, but in reference to the Supports
function, which has the same limitation as the as
operator.
The solution is to use RTTI to fetch the interface's GUID, and then use that to type-cast the interface value. You could use Supports
:
guid := GetTypeData(TypeInfo(T))^.Guid;
success := Supports(IUnknown(rgvar), guid, FCurrent);
Assert(success);
You could also call QueryInterface
directly:
guid := GetTypeData(TypeInfo(T))^.Guid;
OleCheck(IUnknown(rgvar).QueryInterface(guid, FCurrent));
Upvotes: 6