Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is this “cyclic inheritance” called in Delphi?

I saw a code like this the other day:

type
    TcxGridTableControllerAccess = class (TcxGridTableController);

    TMycxGridDBTableView = class (TcxGridDBTableView)
    protected
        function GetViewDataClass: TcxCustomGridViewDataClass; override;
    end;

    TMycxGridViewData = class (TcxGridViewData)
    protected
        function GetFilterRowClass: TcxGridFilterRowClass; override;
    end;

    TMycxGridFilterRow = class (TcxGridFilterRow)
    protected
        procedure SetValue(Index: Integer; const Value: Variant); override;
    end;

    TcxGridDBTableView = class (TMycxGridDBTableView);

TMycxGridDBTableView inherited from TcxGridDBTableView that inherited from TMycxGridDBTableView. Searched for Cyclic Inheritance but only Java results.

What is this called?

Ps: I don't have the full buildable code with me.

like image 862
Lucas Steffen Avatar asked Dec 06 '22 17:12

Lucas Steffen


2 Answers

The example code doesn't do what you think it does. You see TMycxGridDBTableView being defined as a descendant of TcxGridDBTableView, and then you see TcxGridDBTableView, defined as a descendant of TcxGridDBTableView.

However, the TcxGridDBTableView you see at the top is not the same TcxGridDBTableView that you see later. The first one refers to a class declared elsewhere, in some other unit. The next occurrence is declaring a new class in this unit that happens to have the same base name as the other unit's class.

This technique is known as an interposer class. It's used to introduce a new GetViewDataClass method, but still end up with the same class name. The form that uses controls with that name will use the new version of the class instead of the original version. It's a way to customize a VCL control without having to compile and install a custom package.

like image 151
Rob Kennedy Avatar answered Jan 01 '23 02:01

Rob Kennedy


What you show is not cyclic inheritance. What happens is that dxSample.TMycxGridDBTableView inherits from a TcxGridDBTableView in another unit, probably cxGridDBTableView.TcxGridDBTableView. And dxSample.TcxGridDBtableView inherits from dxSample.TMycxGridDBTableView.

Your code is equivalent to:

type
  TcxGridTableControllerAccess = class(TcxGridTableController);

  { Note: this does NOT inherit from the TcxGridDBTableView defined  }
  { a little further on in the source. It inherits from the original }
  { DevEx TcxGridDBTableView.                                        }

  TMycxGridDBTableView = class(cxGridDBTableView.TcxGridDBTableView)
  protected
    function GetViewDataClass: TcxCustomGridViewDataClass; override;
  end;

  TMycxGridViewData = class(TcxGridViewData)
  protected
    function GetFilterRowClass: TcxGridFilterRowClass; override;
   end;

  TMycxGridFilterRow = class(TcxGridFilterRow)
  protected
    procedure SetValue(Index: Integer; const Value: Variant); override;
  end;

  TcxGridDBTableView = class(TMycxGridDBTableView);

So the hierarchy is:

cxGridDBTableView.TcxGridDBTableView
                 |
                 v
   dxSample.TMycxGridDBTableView
                 |
                 v
    dxSample.TcxGridDBTableView 

So dxSample.TMycxGrdiDBTableView does not inherit from dxSample.TcxGridDBTableView, but from cxGridDBTableView.TcxGridDBTableView instead, so there is no so called cyclic inheritance there. The whole misunderstanding comes from the fact that the two classes in the different units have the same name and that the first declaration does not fully qualify the class it is inheriting from.

Now, if someone puts the unit dxSample after cxridDBTableView in his or her uses clause, then dxSample.TCxGridDBTableView is used, instead of the original DevEx class. This is called interposing.

When people want to modify the behaviour of the VCL and FireMonkey, it is not unusual to see interposer classes like

type
  TVCLClass = class(OriginalVCLUnit.TVCLClass)
    // modifications to the original TVCLClass
  end;

or

type
  TMyVCLClass = class(OriginalVCLUnit.TVCLClass)
    //
  end;

  TVCLClass = class(TMyVCLCLass);

The code you showed does the latter.

like image 26
Rudy Velthuis Avatar answered Jan 01 '23 00:01

Rudy Velthuis