将泛型类转换为delphi接口
我从一个.NET类库中得到了一个IEnumVariant,我试图使用一个泛型类将其转换为IEnumerator 尝试将I接口强制转换为泛型类型T时出现编译器错误,“运算符不适用于此操作数类型” 我在尝试将cast类型转换为类时见过一些变通方法,但这些方法对接口不起作用 使用Rob建议的支持似乎有问题,TypeInfo为参数化类型返回nil将泛型类转换为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
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)谢谢你,罗布。“支持”语句是错误的。更正后,我得到一个E2010不兼容的类型:检索Guid@Clint你试过通过理解代码来解决这个问题吗?@David我已经解决了,并编辑了这个问题,包括这个方法不适合我。我应该在这里加一句评论。我真的在提醒Rob代码没有编译。我很感激像你和Rob&Remy这样的人所给予的帮助。我已经修复了Rob的参数顺序,并确认了他答案中的代码是有效的。谢谢@david。重新检查后,我意识到我在调用mscorlib中的typeinfo,这是编译问题的根源,返回nil
guid := GetTypeData(TypeInfo(T))^.Guid;
success := Supports(IUnknown(rgvar), guid, FCurrent);
Assert(success);
guid := GetTypeData(TypeInfo(T))^.Guid;
OleCheck(IUnknown(rgvar).QueryInterface(guid, FCurrent));