我有一个TList<TForm>类型的列表。我需要像这样将其强制转换为TList<TObject>使用:

procedure mainForm.testCast;
var
  listT: TList<TForm>;
  listW: TList<TObject>;
  obj: TObject;
begin
  listT := TList<TForm>.create;
  listT.add(form1);
  listT.add(form2);

  listW := TList<TObject>(listT);  // Casting is OK

  // This works, but is this fine?
  for obj in listW do
    memo1.lines.add(obj.className);

end;


该示例按预期工作,但是可以在通用列表之间进行这样的转换吗?这会导致某些数据结构损坏等吗?
我仅将其用于循环(DoGetEnumerator)用途和一些字符串检查,即,我不会添加/删除项目。

真正的功能稍微复杂一些。它在listT中使用RTTI获取对TValue的引用。
主要目标不是在我的设备中链接FMX.Forms

更新:
Why are TGeneric<Base> and TGeneric<Descendant> incompatible types?

最佳答案

好的,您的代码可以工作,但是在我看来,这有点可疑。简单地说演员表是不合法的,因为

TList<TForm>.InheritsFrom(TList<TObject>)


是错误的。因此,TList<TForm>对象不是TList<TObject>。如果是这样,则将不需要演员表。

之所以如此,是因为Delphi的泛型类型是不变的。更多详情可在这找到:
Why is a class implementing an interface not compatible with the interface type when used in generics?

如果您很难理解设计者为何使泛型类型不变,请暂时考虑在代码中编写listW.Add(TObject.Create)的效果。想一想这对TList<TForm>类型的真实基础对象意味着什么。

所以语言不会给您任何承诺。您正在冒险超出其保证范围。碰巧这两种不相关的类型的实现足够兼容,因此您的代码可以正常工作。但这确实只是实施的偶然事件。

由于您已经在使用RTTI,因此建议您使用RTTI遍历该列表。您可以使用RTTI调用GetEnumerator等。这样,您将调用对象的实际方法。

关于delphi - 将TList <T:class>转换为TList <W:class>,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/21677081/

10-11 10:34