Steve Marsh
Steve Marsh

Reputation: 1

Inherited class function results in 'Incompatible types'

We have a software project (Delphi XE5) that runs perfectly fine. It basically displays a diagram with various elements. Now in order to expand it, we have to reimplement the drawing methods, which also works fine. However, we then run into incompatible type problems for other functions in the inherited class.

Unit that describes the diagram elements:

unit Elements;

type
TElementA = class
  procedure DrawSomething; virtual;
end;

TElementB = class
  FElementA: TElementA;
  procedure DrawSomething; virtual;
  function ReturnSomething: TElementA;
end;

The second unit uses the first one and reimplements basically the drawing procedures:

unit ElementsDiff;

uses Elements;

type
TElementA = class(Elements.TElementA)
  procedure DrawSomething; override;
end;

TElementB = class(Elements.TElementB)
  procedure DrawSomething; override;
end;

The procedure that uses those classes looks the following:

unit Main;

uses
  Elements,
  ElementsDiff;

implementation

procedure MainProcedure;
var
  FElementA: TElementA;
  FElementB: TElementB;
begin
  FElementA := TElementA.Create;
  FElementB := TElementB.Create;
  // do some stuff...
  // ................

  FElementA := FElementB.ReturnSomething;
end;

When compiling, of course it doesn't compile cause of the last line. We get a nice error:

Incompatible types: 'ElementsDiff.TElementA' and 'Elements.TElementA'

meaning: ReturnSomething returns type 'Elements.TElementA', but FElementA is of type 'ElementsDiff.TElementA'. I understand the cause of the problem (it's been answered numerous times here already), and the simplest solution would be to just do a typecast, i.e.

FElementA := TElementA(FElementB.ReturnSomething);

But since it's a rather big software project, much much more complex than the code above, I wonder if there's actually a better solution than to just typecast every time that function is used.

Any thoughts on how to solve this in a nice way are much appreciated.

Upvotes: 0

Views: 1654

Answers (3)

GolezTrol
GolezTrol

Reputation: 116110

The last unit that is inluded 'owns' the type name in case of a collision. Since that is the unit ElementsDiff, containing the descendant classes, your main code uses those classes.

The problem then, is that ReturnSomething returns an instance the base class, Elements.TElementA.

To solve this, you can (amongst others) declare your variables in Main like this:

var
  FElementA: Elements.TElementA;
  FElementB: Elements.TElementB;

That way, the variables can contain either the base class or the descendant class without problems, as long as you don't use any methods or properties that only exist in the descendants.

But maybe it's better to give the diff classes a different name to avoid confusion. The classes don't have to have the same name. This will work too:

// In ElementDiff:
TElementBDiff = class(TElementB)

// In Main:
var
  ElementB: TElementB;
begin
  ElementB := TElementBDiff.Create;

Upvotes: 1

David Dubois
David Dubois

Reputation: 3932

I think you're looking for something like this:

TElementB = class(Elements.TElementB)
  procedure DrawSomething; override;
  function ReturnSomething: tElementA; reintroduce;
end;

Just be careful that when you implement the new ReturnSomething that you always return an object of type ElementsDiff.tElementA.

I would recommend using AS rather than explicit typecasting.

FElementA := FElementB.ReturnSomething as tElementA;

This will ensure that the object is really of the type you're expecting.

Upvotes: 0

Toon Krijthe
Toon Krijthe

Reputation: 53366

The easiest way to solve these nameclashes is by providing a local alias in unit Main.:

type
  TElementA = Elements.TElementA;
  TElementB = Elements.TElementB;

This way, the compiler knows which TElementA you want to use.

Upvotes: 0

Related Questions