Jens Mühlenhoff
Jens Mühlenhoff

Reputation: 14873

How to avoid an incompatible types error with a non parameterless constructor in a generic container?

I get E2010 Incompatible types: 'TDerivedFrame' and 'TBaseFrame' on the following code:

type
  TBaseFrame = class(TFrame)
  end;

  TDerivedFrame = class(TBaseFrame)
  end;

  TContainer<T: TBaseFrame> = class
  private
    FFrame: T;
  public
    property Frame: T read FFrame;
    constructor Create;
  end;

constructor TContainer<T>.Create;
begin
  inherited;
  FFrame := TBaseFrame(T).Create(nil);
end;

var
  FTab: TContainer<TDerivedFrame>;

Using only T.Create(nil) causes E2568 Can't create new instance without CONSTRUCTOR constraint in type parameter declaration.

As far as I know you can only create a constructor constraint with a parameterless constructor.

What is the correct way to do this?

PS: The code compiles when I remove the variable which makes me think this is a compiler error?!

Edit: I understand the E2010 error, but even with T(TBaseFrame(T).Create(nil)) it doesn't work. This compiles, but causes an access violation at runtime:

type
  TBaseFrame = class(TFrame)
  public
    constructor Create(AOwner: TComponent); override;
  end;

  TDerivedFrame = class(TBaseFrame)
  public
    constructor Create(AOwner: TComponent); override;
  end;

  TContainer<T: TBaseFrame> = class
  private
    FFrame: T;
  public
    property Frame: T read FFrame;
    constructor Create;
  end;


constructor TBaseFrame.Create(AOwner: TComponent);
begin
  inherited;

end;

constructor TDerivedFrame.Create(AOwner: TComponent);
begin
  inherited;

end;

constructor TContainer<T>.Create;
begin
  inherited;
  FFrame := T(TBaseFrame(T).Create(nil));
end;

var
  FTab: TContainer<TDerivedFrame>;
begin
  FTab := TContainer<TDerivedFrame>.Create;
end.

Upvotes: 3

Views: 1961

Answers (2)

user1989740
user1989740

Reputation: 1

Try to use this one: TContainer<T:constructor, TBaseFrame> = class

Upvotes: 0

David Heffernan
David Heffernan

Reputation: 612993

FFrame := T(TBaseFrameClass(T).Create(nil));

is the correct way to do this.

You'll need

type 
  TBaseFrameClass = class of TBaseFrame;

Your compiler error is because you are instantiating a type, TContainer<TDerivedFrame>. When you instantiate that type you ask the compiler to deal with

FFrame := TBaseFrame(...);

where FFrame is TDerivedFrame. If you wrote that code out without generics you'd understand the compiler error.

So, the compiler is behaving correctly here, but the issue of instantiation is catching you out. It's only at the point of instantiation that the compiler error becomes apparent. Without instantiation, of if you had instantiated TContainer<TBaseFrame> there would be no compiler error.

The AV is because you were casting T to be an instance rather than a class.

Upvotes: 5

Related Questions