0

I have a problem of circular reference between class. Here is the error:

[dcc32 Fatal Error] StackWeb.MessageClassHelper.pas(5): F2047 Circular unit reference to 'StackWeb.MessageClass'

I'd like to use both class in different units.

How could I solve it?

unit StackWeb.MessageClass;

interface

uses
  StackWeb.ChatItemClass,

type    
  TMessageClass = class(TCardDefault)
  private
    FChat : TChatItemClass;
  public
    property chat : TChatItemClass read FChat write FChat;
  end;

unit StackWeb.ChatItemClass;

interface

uses
  StackWeb.MessageClass;

type    
  TChatItemClass = class(TClassStandardList<TMessageClass>)
  private
    FMessages : TArray<TMessageClass>;
  public
    property messages : TArray<TMessageClass> read FMessages write FMessages;
  end;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Luiz Alves
  • 2,575
  • 4
  • 34
  • 75
  • 1
    The only way you're able to successfully have a circular referenced unit is if one of them use "used" from the `implementation` section instead of the `interface`. – Jerry Dodge Jan 28 '20 at 15:49
  • @Jerry Dodge I can´t do it, because I need to use in the interface section – Luiz Alves Jan 28 '20 at 15:54
  • @LuizAlves a *forward declaration* is the ONLY way to break circular unit dependancy without otherwise redesigning your classes to not depend on each other at all – Remy Lebeau Jan 28 '20 at 16:21

1 Answers1

1

The declaration of the TMessageClass class does not need to know the specifics of how the TChatItemClass class is declared, only that it is a class type, so use a forward declaration to break the circular unit dependancy of the two interface sections, eg:

unit StackWeb.MessageClass;

interface

{uses
  StackWeb.ChatItemClass;}

type
  TChatItemClass = class; // <-- HERE

  TMessageClass = class(TCardDefault)
  private
    FChat : TChatItemClass;
  public
    property chat : TChatItemClass read FChat write FChat;
  end;

implementation

uses
  StackWeb.ChatItemClass; // <-- MOVED HERE

...

end.
unit
  StackWeb.ChatItemClass;

interface

uses
  StackWeb.MessageClass;

type
  TChatItemClass = class(TClassStandardList<TMessageClass>)
  private
    FMessages : TArray<TMessageClass>;
  public
    property messages : TArray<TMessageClass> read FMessages write FMessages;
  end;

...

end.

This scenario is actually covered in Delphi's documentation:

Classes and Objects (Delphi): Forward Declarations and Mutually Dependent Classes

If the declaration of a class type ends with the word class and a semicolon - that is, if it has the form

type className = class;

with no ancestor or class members listed after the word class, then it is a forward declaration. A forward declaration must be resolved by a defining declaration of the same class within the same type declaration section. In other words, between a forward declaration and its defining declaration, nothing can occur except other type declarations.

Forward declarations allow mutually dependent classes. For example:

type
  TFigure = class; // forward declaration
  TDrawing = class
    Figure: TFigure;
    // ...
  end;

  TFigure = class // defining declaration
    Drawing: TDrawing;
    // ...
  end; 

Do not confuse forward declarations with complete declarations of types that derive from System.TObject without declaring any class members.

type
  TFirstClass = class; // this is a forward declaration

  TSecondClass = class // this is a complete class declaration
  end;

  TThirdClass = class(TObject); // this is a complete class declaration
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • @Remy Lebeau Thank you Remy, but It does not working. I get [dcc32 Error] StackWeb.ChatItemClass.pas(10): E2086 Type 'TMessageClass' is not yet completely defined – Luiz Alves Jan 28 '20 at 16:08
  • @LuizAlves well, that is because you are using `TMessageClass` inside of Generics, which don't work on partial types. I updated my answer. You should only need the forward declaration on the non-Generic side to break the circular unit dependancy. – Remy Lebeau Jan 28 '20 at 16:24
  • @ Remy Lebeau I am using different units to classes. Your post doent work to me. I already read about embarcadero notes abou it . All links posted from David does not solve my problem – Luiz Alves Jan 28 '20 at 16:29
  • @remy No. The code doesn't compile because you have a forward definition which you fail to define later in that type section. – David Heffernan Jan 28 '20 at 20:32