views:

329

answers:

1

I struggle a little with the understanding of generics and how they can and can not be used.

I have a generic class TControlMediator like this:

TControlMediator<C, T> = class
private
  FMediatedComponent: C;
public
  constructor Create(ComponentToMediate: C);

  function GetValue: T; virtual; abstract;
  procedure SetValue(Value: T); virtual; abstract;

  property MediatedControl: C read FMediatedComponent;
end;

I then make 'concret' subclasses for each control type that I want to mediate:

TEditMediator = class(TControlMediator<TEdit, string>)
public
  function GetValue: string; override;
  procedure SetValue(Value: string); override;
end;

So far, everything seems to work OK. Problems arise, though, when I want a list of TControlMediator descendants, or taking a TControlMediator as an parameter to a method:

TViewMediator = class
private
  FControlMediators: TList<TControlMEdiator<C, T>>;
public
  procedure registerMediator(AControlMediator: TControlMediator<C, T>);
  procedure unregisterMediator(AControlMediator: TControlMediator<C, T>);
end;

The compiler stops with fatal errors:

[DCC Error] mediator.pas(23): E2003 Undeclared identifier: 'C'
[DCC Error] mediator.pas(28): E2007 Constant or type identifier expected

Do anybody have any clues on how this is supposed to be done?

+4  A: 

Delphi has no covariance or contravariance on its generic types. Your generic types must use actual types as parameters. In other words, this:

TViewMediator = class
private
  FControlMediators: TList<TControlMEdiator<C, T>>;
public
  procedure registerMediator(AControlMediator: TControlMediator<C, T>);
  procedure unregisterMediator(AControlMediator: TControlMediator<C, T>);
end;

...won't work because C and T are not generic type arguments to TViewMediator or actual types.

TControlMediator<TEdit, string> is a type. TList<TControlMEdiator<C, T>>, when there are no types C or T in scope is not a type. You can't use a generic type placeholder in an instantiation of a generic type unless those placeholders are in scope as generic arguments to the containing generic type or method.

Craig Stuntz
So I'm basically screwed, then? My list has to be a good old TList of TObjects, and there is no way of casting the objects in it to an TControlMediator<C, T> so that I can call methods on it?
Vegar
I can't say if you're screwed until I understand exactly why you want to do this. Could you just parameterize as `TViewMediator<C, T>` ?
Craig Stuntz
I want to register mediators of different kind like: MyViewMediator.RegisterMediator(TEditMediator.create(someEdit)); MyViewMediator.RegisterMediator(TComboMediator.create(someCombo));Later, I want to loop all registered mediators and call a method on them.
Vegar
You can give your generic type a non-generic parent type, and make your list a list of that.
Craig Stuntz
Yes, that's the solution that I have come to. Maybe even an interface.
Vegar
Yes, an interface would work, too.
Craig Stuntz