【问题标题】:Casting in generic class to interface delphi将泛型类转换为接口delphi
【发布时间】:2023-03-22 21:05:01
【问题描述】:

我从 .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); 

没有

【问题讨论】:

  • 当询问编译器错误时,您应该始终包含错误
  • 我猜你可以使用 TValue 来完成这项工作
  • 仅供参考,COM 接口不会通过GetLastError() 报告错误,因此使用RaiseLastOSError() 是错误的。请改用OleCheck(),例如:OleCheck(FDotNetEnum.Next(1, rgvar, fetched));OleCheck(FDotNetEnum.Reset);

标签: delphi generics delphi-xe6


【解决方案1】:

使用as 转换接口仅对具有 GUID 的接口有效。编译器在编译泛型类时不能假定 T 具有 GUID,因此它不能接受 val as T 形式的表达式。

This has been covered before, but in reference to the Supports function,其限制与as 运算符相同。

解决方案是使用 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));

【讨论】:

  • 谢谢罗伯。支持声明是错误的。更正后,我得到一个 E2010 不兼容的类型:'PTypeInfo' 和 '_TypeInfo' 在检索 Guid 时
  • @Clint 你有没有试过通过理解代码来解决这个问题?
  • @David 我已修复它并编辑了问题以包括此方法对我不起作用。我应该在这里添加评论。我真的在提醒 Rob 代码没有编译。我很感激像你和 Rob & Remy 这样的人所给予的帮助。
  • 我已经修复了 rob 的参数顺序,并确认他的答案中的代码有效。
  • 谢谢@david。重新检查后,我意识到我在 mscorlib 中调用 typeinfo,这是编译问题的根源并返回 nil
猜你喜欢
  • 2016-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-16
  • 2013-01-08
相关资源
最近更新 更多