8

Having the following generics class

 TTuple<T1, T2> = class
  protected
    fItem1: T1;
    fItem2: T2;
  public
    constructor Create; overload;
    constructor Create(Item1: T1; Item2: T2); overload;
    destructor Destroy; override;

    property Item1: T1 read fItem1 write fItem1;
    property Item2: T2 read fItem2 write fItem2;
  end;

constructor TTuple<T1, T2>.Create;
begin
  inherited;
end;

constructor TTuple<T1, T2>.Create(Item1: T1; Item2: T2);
begin
  fItem1 := Item1;
  fItem2 := Item2;
end;

destructor TTuple<T1, T2>.Destroy;
begin
  inherited;
end;

and used in a manner like:

x := TTuple<TObjectList<TMyObjects>, Integer>.Create;

I need to free fitem1 manually. How can I free the fItem1 inside the destructor?

RBA
  • 12,337
  • 16
  • 79
  • 126
  • 2
    Just write T1: class instead of T1 and you got the trick. In that way you have a "less generic" generic because T1 can only be a class type; since the compiler is aware of this fact, you'll be able to call Free – Alberto Miola Mar 03 '18 at 11:09
  • 1
    You can learn a lot of tricks for using Generics in Delphi looking at the source of `System.Generics.Collections`. `TObjectList` and `TObjectDictionary` will show you how the RTL uses the techniques outlined in @AndreiGalatyn [response](https://stackoverflow.com/a/49083469/859646). – JRL Mar 03 '18 at 11:17
  • 2
    What does this class give you? It seems to offer an extra later of complexity for no benefits that I can discern. Or is it just an illustration? – David Heffernan Mar 03 '18 at 13:06

1 Answers1

9

In the definition of TTuple there is no restrictions on type of T1,T2. That is why you can't call destructor, because it can be any type, double/integer etc. Direct answer to your question:

  PObject(@fItem1).DisposeOf;

But it will work properly only when T1 is class. Proper solution is to define TTuple with restrictions on type:

TTuple<T1: class; T2> = class

Then you can free it in normal way:

fItem1.Free

To make it in Delphi-like style you can create two generic classes:

TTuple<T1,T2> = class
...
end;

TObjectTuple<T1: class; T2> = class<TTuple<T1,T2>>
  ...
  property OwnsKey: boolean;
end;

destructor TObjectTuple<T1,T2>.destroy;
begin
  if OwnsKey then
    FItem1.DisposeOf;
end;

For example look how it is implemented in

TObjectList<T: class>
Rudy Velthuis
  • 28,387
  • 5
  • 46
  • 94
Andrei Galatyn
  • 3,322
  • 2
  • 24
  • 38