Tomasz Rutkowski
Tomasz Rutkowski

Reputation: 61

I have two units and a class in each unit that need each other to function. How do I avoid a circular reference?

I have 2 classes, ClassA and ClassB, each in their own seperate unit, UnitA and UnitB.

UnitA uses UnitB. This allows ClassA to call the constructor for ClassB. Part of ClassB's functionality however requires the use of ClassA's methods. I cannot make UnitB use UnitA, else this causes a circular reference. What alternative do I have in allowing ClassB to access ClassA's methods from within UnitB?

Upvotes: 5

Views: 2051

Answers (3)

Pieter B
Pieter B

Reputation: 1967

If you have ClassA and ClassB and both use methods in ClassA, then you may want to refactor those methods out of ClassA into their unit (and class if you fancy).

Upvotes: 0

Dsm
Dsm

Reputation: 6013

To expand a little on the previous answer, another possiblity (although definitely less readable) is to refer to an ancestor that both have acess to ( in the extreme case TObject) and then cast in the implementation stage. So

interface

TClassA = class
...
fRefToClassB : TObject;  // internal ref
...
procedure UsingBRef( pVar : TObject );
...
end;

implementation

procedure TClassA.UsingClassB
begin
  with fRefToClass as TClassB do
...
end;

procedure TClassB.UsingBRef( pVar : TObject );
begin
  with pVar as TClassB do
...
end;

Obviously there are many variations on a theme and you would implement is better than this. Again I would emphasise the previous solution is better, but this provides a get out when all else fails.

Upvotes: 2

Jerry Dodge
Jerry Dodge

Reputation: 27276

It depends a lot on how the two classes need to use each other. If one class uses the other in the implementation, but not the interface, then you can move that unit in the uses clause from the interface section down to the implementation section instead.

This would work either way around:

UnitA;

interface

uses
  Classes, UnitB;

type
  TClassA = class(TObject)
  end;

...

.

UnitB;

interface

uses
  Classes;

type
  TClassB = class(TObject)
  end;

implementation

uses
  UnitA;

...

However, if the interface of both your classes rely on each other, then you have no choice but to put both classes in the same unit (and using forward declarations).

UnitA;

interface

uses
  Classes;

type
  TClassA = class;
  TClassB = class;


  TClassA = class(TObject)
  end;

  TClassB = class(TObject)
  end;

...

In rare cases, you might even be able to move an entire class definition down to implementation. That wouldn't be useful though if you needed to actually use that class elsewhere.

UnitB;

interface

implementation

uses
  Classes, UnitA;

type
  TClassB = class(TObject)
  end;

...

Depending on the complexity, it's also common practice to put commonly shared code in a separate unit of its own. For example constants, record array types, global variables, etc. This common unit shall not use any other of these units.

Upvotes: 9

Related Questions