将泛型类转换为接口 Delphi

3

我从一个.NET类库获取了一个IEnumVariant,现在我想使用通用类将其转换为IEnumerator。

当尝试将IInterface强制转换为通用类型T时,会出现编译器错误,即“此操作数类型不适用于运算符”。我已经看到了一些解决方案,但这些解决方案并不适用于接口。

如Rob所建议的那样使用Supports似乎也存在问题,因为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;

我是否错过了某些东西,以使那个案例能够适用于通用接口类型?

我有另一种构造函数,可以从中获取guid。

TDotNetEnum<IContact>.Create(vContactList, IContact);

工作正常但不是理想状态

TDotNetEnum<IContact>.Create(vContactList); 

不会


当询问编译器错误时,您应该始终包含错误信息。 - David Heffernan
我的猜测是,您可以使用TValue来完成这个任务。 - David Heffernan
2
知道一下,COM接口不会通过 GetLastError() 报告错误,因此使用 RaiseLastOSError() 是错误的。应该使用 OleCheck(),例如:OleCheck(FDotNetEnum.Next(1, rgvar, fetched));OleCheck(FDotNetEnum.Reset); - Remy Lebeau
1个回答

6
使用as类型转换接口仅适用于具有GUID的接口。编译器在编译泛型类时无法假设T具有GUID,因此不能接受形式为val as T的表达式。 这个问题之前已经有过解答,但是与Supports函数相同,该函数也存在相同的限制问题。 解决方案是使用RTTI获取接口的GUID,然后使用它来强制转换接口值。您可以使用Supports
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));

谢谢Rob。Supports语句的顺序错了。纠正后,当检索Guid时,我得到了一个E2010不兼容类型的错误:'PTypeInfo'和'_TypeInfo'。 - Clint Good
@Clint 你尝试过通过理解代码自行解决这个问题吗? - David Heffernan
@David,我已经修复了它,并编辑了问题以包括这种方法对我无效的事实。我应该在这里添加一个评论。我真的是在提醒Rob代码无法编译。我非常感谢像你、Rob和Remy这样的人所给予的帮助。 - Clint Good
我已经修复了Rob的参数顺序,并确认他答案中的代码可以工作。 - David Heffernan
谢谢@david。重新检查后,我意识到我在mscorlib中调用了typeinfo,这是编译问题的源头,并返回了nil。 - Clint Good

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接