I have a list of type TList<TForm>. I need to cast it and use it as TList<TObject> like this:

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;

The sample works as expected, but is it ok to cast like this between generic lists? Will this cause some data structure corruption etc? I use it only for looping (DoGetEnumerator) purposes and some string checks i.e. I'll not add/remove items.

The real function is little more complicated. It gets reference to listT using RTTI in a TValue. The main goal is not to link FMX.Forms in my unit.

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

有帮助吗?

解决方案

Well, your code will work, but it somewhat dubious in my view. Simply put the cast is not legal because

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

is false. So a TList<TForm> object is not a TList<TObject>. If it were, then the cast would not be needed.

That this is so is because Delphi's generic types are invariant. More details can be found here: Why is a class implementing an interface not compatible with the interface type when used in generics?

If you have any difficulty understanding why the designers made generic types invariant, consider for a moment the effect of writing listW.Add(TObject.Create) in your code. Think what it means to the true underlying object of type TList<TForm>.

So the language promises you nothing. You are venturing outside its guarantees. It so happens that the implementation of these two un-related types is compatible enough for your code to work. But that is really just an accident of implementation.

Since you are already using RTTI, then I suggest that you iterate over the list with RTTI. You can call GetEnumerator and so on using RTTI. That way you will call the actual methods of the object.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top