Question

I am trying to implement an interface to convert records in a dataset to Delphi records in a pre-generics version of Delphi. I don't like the interface at the moment, as it will always need calls to Supports which I'd like to avoid if possible and was wondering if there's a better way of doing it that I'm missing.

So far I have an navigation interface and data retrieval interface defined:

IBaseRecordCollection = interface
  procedure First;
  procedure Next;
  function BOF: boolean;
  ... // other dataset nav stuff
end;

IRecARecordCollection = interface
  function GetRec: TRecA;
end;

IRecBRecordCollection = interface
  function GetRec: TRecB;
end;

Basically I have a concrete base class that contains a private dataset and implements IBaseRecordCollection and concrete class for each RecordCollection interface which derives from an abstract class implementing the IBaseRecordCollection (handled by an implements property) with the implementation of the record retrieval routine:

TAbstractTypedRecordCollection = class(TInterfacedObject, IBaseRecordCollection)
private
  FCollection: IBaseRecordCollection;
protected
  property Collection: IBaseRecordCollection read FCollection implements IBaseRecordCollection;
public
  constructor Create(aRecordCollection: IBaseRecordCollection);
end;

TRec1RecordCollection = class(TAbstractTypedRecordCollection, IRecARecordCollection);
public
  function GetRec: TRecA;
end;

Now, to use this I'm forced to have a builder that returns a IRecARecordCollection and then mess around with Supports, which I'm not keen on as it will always be used in this fashion.

i.e.

procedure GetMyRecASet;
var
  lRecARecordCollection: IRecARecordCollection;
  lRecordCollection: IBaseRecordCollection;
begin
  lRecARecordCollection := BuildRecACollection;
  if not supports(lRecARecordCollection, IBaseRecordCollection, lRecordCollection) then 
     raise exception.create();
  while not lRecordCollection.EOF do
  begin
    lRecARecordCollection.GetRec.DoStuff;
    lRecordCollection.Next;
  end;
end;

Although this works, I'm not keen on the supports call and mixing my lRecordCollections and my lRecARecordCollections like this. I had originally hoped to be able to do something like:

IBaseRecordCollection = interface
  // DBNav stuff
end;

IRecARecordCollection = interface (IBaseRecordCollection)
  function GetRec: TRecA;
end;

TRec1RecordCollection = class(TInterfacedObject, IRecARecordCollection)
private
  FCollection: IBaseRecordCollection;
protected
  property Collection: IBaseRecordCollection read FCollection implements IBaseRecordCollection;
public
  function GetRec: TRecA;
end;

but unfortunately Delphi wasn't smart enough to realise that the implementation of IRecARecordCollection was split over the base IBaseRecordCollection in the Collection property implements call and the TRec1RecordCollection object.

Are there any other suggestions for neater ways to acheive this?

-- edit to give a (longer) reply to @David's answer than possible in a comment

The suggested solution of:

IBaseRecordCollection = interface ['{C910BD0A-26F4-4682-BC82-605C4C8F9173}']
  function GetRecNo: integer;
  function GetRecCount: integer;
  function GetFieldList: TFieldList;
  function EOF: boolean;
  function BOF: boolean;
  ...
end;

IRec1RecordCollection = interface (IBaseRecordCollection) ['{E12F9F6D-6D57-4C7D-AB87-8DD50D35DCA2}']
  function GetRec: TRec1;
  property Rec: TRec1 read GetRec;
end;

TAbstractTypedRecordCollection = class(TInterfacedObject, IBaseRecordCollection)
private
  FCollection: IBaseRecordCollection;
protected
  property Collection: IBaseRecordCollection read FCollection implements IBaseRecordCollection;
public
  constructor Create(aRecordCollection: IBaseRecordCollection);
end;

TRec1RecordCollection = class(TAbstractTypedRecordCollection, IRec1RecordCollection, IBaseRecordCollection)
private
  function GetRec: TRec1;
public
  property Rec: TRec1 read GetRec;
end;

isn't compiling. It's complaining that TRec1RecordCollection cannot find methods related to IBaseRecordCollection. I also tried moving the Collection property from Abstract to Rec1RecordCollection and redeclaring the property in TRec1RecordCollection all with the same result

Looking a bit deeper it appears that direct inheritance of a class implementing IBaseRecordCollection would work but Delphi can't handle doing it indirectly via a property using implements.

Was it helpful?

Solution

Your code is almost there. The implements directive in your code fails to compile because you only declared that your class implements the derived interface. As it stands, your class does not implement the interface that the implements directive refers to, namely IBaseRecordCollection. You might think that would be inferred from the inheritance but it is not.

To solve your problem you simply need to declare that TRec1RecordCollection implements both interfaces:

type
  TRec1RecordCollection = class(TInterfacedObject, IBaseRecordCollection, 
    IRecARecordCollection)
  ....
  end;

Make just the one small change and your code will compile.

Update

Your edit to the question changes this somewhat. The code in my answer does indeed compile, given the code in your original question. However, add any method into IBaseRecordCollection and the compile will not accept it.

The compiler should accept this code and the fact that it does not is because of a compiler bug. Modern versions of Delphi will accept the code in your update to the question.

Unless you upgrade your compiler you will not be able to make your intended design work.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top