4

I'm trying to implement Spring 4 Delphi and only program to interfaces instead of classes. However this seems impossible when you want to use a TObjectList.

Consider the following code:

unit Unit1;

interface

uses
  Spring.Collections,
  Spring.Collections.Lists;

type

  IMyObjParent = interface
  ['{E063AD44-B7F1-443C-B9FE-AEB7395B39DE}']
    procedure ParentDoSomething;
  end;

  IMyObjChild = interface(IMyObjParent)
  ['{E063AD44-B7F1-443C-B9FE-AEB7395B39DE}']
    procedure ChildDoSomething;
  end;

implementation

type
  TMyObjChild = class(TInterfacedObject, IMyObjChild)
  protected
    procedure ParentDoSomething;
  public
    procedure ChildDoSomething;
  end;


{ TMyObj }

procedure TMyObjChild.ChildDoSomething;
begin

end;

procedure TMyObjChild.ParentDoSomething;
begin

end;

procedure TestIt;
var
  LMyList: IList<IMyObjChild>;
begin
  TCollections.CreateObjectList<IMyObjChild>;
  //[DCC Error] Unit1.pas(53): E2511 Type parameter 'T' must be a class type
end;

end.

I know I can change IMyObjChild to TMyObjChild in the example above, but if I need that in another unit or a form then how do I do this?

Trying to program only to interfaces seems too hard or impossible as soon as you need a TObjectList.

Grrr... Any ideas or help?

Rick Wheeler
  • 1,142
  • 10
  • 22
  • 3
    You can use TInterfaceList. – Toon Krijthe Mar 27 '14 at 06:20
  • 2
    If you use interfaces you don't need a TObjectList because all it does over a TList is manage the lifetime of the objects it is holding. – Stefan Glienke Mar 27 '14 at 06:51
  • Thank you! I've been toiling over this for days, but since the Interface is reference counting, TList can used instead of TObjectList. – Rick Wheeler Mar 27 '14 at 07:09
  • 1
    FWIW, it looks as if your interfaces have exactly the same GUID. That could become a problem. Each interface should have a different one. Use Shift+Ctrl+G in the editor to generate a new one. – Rudy Velthuis Mar 27 '14 at 14:51

1 Answers1

6

CreateObjectList has a generic constraint that its type parameter is a class:

function CreateObjectList<T: class>(...): IList<T>;

Your type parameter does not meet that constraint since it is an interface. The thing about an object list is that it holds objects. If you take a look at TObjectList in Spring.Collections.Lists you'll see that it also has the generic constraint that its type parameter is a class. And since CreateObjectList is going to create a TObjectList<T>, it must reflect the type constraint.

The raison d'être of TObjectList<T> is to assume ownership of its members through the OwnsObjects. Much in the same way as do the classic Delphi RTL classes of the same name. Of course you are holding interfaces and so you simply do not need this functionality. You should call CreateList instead. A plain TList<T> is what you need, even if you refer to it through the IList<T> interface.

LMyList := TCollections.CreateList<IMyObjChild>;
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • That's perfect, thank you so much to you and Stefan! – Rick Wheeler Mar 27 '14 at 07:09
  • It seems so obvious now, but I've been using TObjectList for so long that when moving to interfaces I just assumed I would need to keep using it. Thanks again, this is actually quite exciting for me :-D – Rick Wheeler Mar 27 '14 at 09:03
  • 1
    It seems very common for me for devs that are used to legacy `Contnrs.TObjectList` to want to use it for everything. Only need to use `TObjectList` if you intend to set `OwnsObjects` to `True`. Otherwise plain generic `TList` is good. Even for objects. The only purpose of `TObjectList` is to own objects. But devs have learnt to use it because in non-generic Delphi it reduced the amount of casting in comparison to plain `TList`. – David Heffernan Mar 27 '14 at 09:06