将泛型类转换为delphi接口

将泛型类转换为delphi接口,delphi,generics,delphi-xe6,Delphi,Generics,Delphi Xe6,我从一个.NET类库中得到了一个IEnumVariant,我试图使用一个泛型类将其转换为IEnumerator 尝试将I接口强制转换为泛型类型T时出现编译器错误,“运算符不适用于此操作数类型” 我在尝试将cast类型转换为类时见过一些变通方法,但这些方法对接口不起作用 使用Rob建议的支持似乎有问题,TypeInfo为参数化类型返回nil uses WinApi.ActiveX, Generics.Collections; type TDotNetEnum<T: IInterface

我从一个.NET类库中得到了一个IEnumVariant,我试图使用一个泛型类将其转换为IEnumerator

尝试将I接口强制转换为泛型类型T时出现编译器错误,“运算符不适用于此操作数类型” 我在尝试将cast类型转换为类时见过一些变通方法,但这些方法对接口不起作用

使用Rob建议的支持似乎有问题,TypeInfo为参数化类型返回nil

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;
使用WinApi.ActiveX、泛型、集合;
类型
TDotNetEnum=class(TInterfacedObject,IEnumerator)
严格保密
FDotNetEnum:IEnumVariant;
F电流:T;
函数MoveNext:布尔;
程序复位;
函数GetCurrent:TObject;
函数IEnumerator.GetCurrent=GenericGetCurrent;
函数GenericGetCurrent:T;
公众的
构造函数创建(const ADotNetObject:OleVariant);
////我可以用这个构造函数让它工作
//构造函数创建(const-ADotNetObject:OleVariant;const-AGUID:TGUID);
结束;
实施
使用System.Rtti、SysUtils、mscorlib_TLB、ComObj;
构造函数tdotnetnum.Create(const ADotNetObject:OleVariant);
变量
netEnum:IEnumerable;
开始
netEnum:=IUnknown(ADotNetObject)作为mscorlib_TLB.IEnumerable;
FDotNetEnum:=netEnum.GetEnumerator();
结束;
函数TDotNetEnum.GenericGetCurrent:T;
开始
结果:=fccurrent;
结束;
函数TDotNetEnum.GetCurrent:TObject;
开始
结果:=无;
结束;
函数TDotNetEnum.MoveNext:布尔值;
变量
rgvar:油变异体;
取材:红衣主教;
ti:TypeInfo;
guid:TGUID;
开始
olcheck(FDotNetEnum.Next(1,rgvar,fetched));
结果:=fetched=1;
如果没有结果的话
F电流:=零
其他的
开始

FCurrent:=IUnknown(rgvar)作为T;// 使用
as
转换接口仅对具有GUID的接口有效。编译器在编译泛型类时不能假定
T
具有GUID,因此它不能接受
val形式的表达式作为T

,它与
as
运算符具有相同的限制

解决方案是使用RTTI获取接口的GUID,然后使用该GUID类型转换接口值。您可以使用
支持

guid := GetTypeData(TypeInfo(T))^.Guid;
success := Supports(IUnknown(rgvar), guid, FCurrent);
Assert(success);
您也可以直接调用
QueryInterface

guid := GetTypeData(TypeInfo(T))^.Guid;
OleCheck(IUnknown(rgvar).QueryInterface(guid, FCurrent));

当询问编译器错误时,您应该始终包括错误。我猜您可以使用TValue来完成此操作。但是,COM接口不会通过
GetLastError()
报告错误,因此使用
RaiseLastError()
是错误的。改为使用,例如:
olcheck(FDotNetEnum.Next(1,rgvar,fetched))
olcheck(FDotNetEnum.Reset)guid := GetTypeData(TypeInfo(T))^.Guid;
success := Supports(IUnknown(rgvar), guid, FCurrent);
Assert(success);
guid := GetTypeData(TypeInfo(T))^.Guid;
OleCheck(IUnknown(rgvar).QueryInterface(guid, FCurrent));