I am using Delphi Pro 10.2.3 Tokyo. I want to create a TDataset wrapper class which I can use to enumerate through a list of IData descendants with a for-in loop. When I try to compile the code below, I get the following error message.
[dcc32 Error] Core.Data.DatasetAdapter.pas(25): E2291 Missing implementation of interface method IEnumerator.GetCurrent
Clearly, GetCurrent is implemented. Any idea how to fix this?
unit Core.Data.DatasetAdapter;
interface
uses
    Data.Db
  ;
type
  IData = interface
    ['{15D1CF4F-B9E1-4525-B035-24B9A6584325}']
  end;
  IDataList<T: IData> = interface
    ['{9FEE9BB1-A983-4FEA-AEBF-4D3AF5219444}']
    function GetCount: Integer;
    function GetCurrent: T;
    procedure Load;
    procedure Unload;
    property Count: Integer read GetCount;
    property Current: T read GetCurrent;
  end;
  TDatasetAdapter<T: IData> = class(
      TInterfacedObject
    , IData, IDataList<T>
    , IEnumerator<T>
  )
  private
    FBof: Boolean;
    FDataset: TDataset;
    FIntf: T;
    function GetCount: Integer;
    function GetCurrent: T;
    function GetEof: Boolean;
    function GetInterface: T;
    function MoveNext: Boolean;
    procedure Reset;
  protected
    function FieldByName(const FieldName: string): TField;
    procedure MapFields; virtual;
    property Dataset: TDataset read FDataset;
  public
    constructor Create(ADataset: TDataset); virtual;
    function GetEnumerator: IEnumerator<T>;
    procedure Cancel;
    procedure Close;
    procedure Delete;
    procedure Edit;
    procedure First;
    procedure Insert;
    procedure Load;
    procedure Next;
    procedure Open;
    procedure Post;
    procedure UnLoad;
    property Count: Integer read GetCount;
    property Eof: Boolean read GetEof;
  end;
implementation
uses
    System.SysUtils
  , System.TypInfo
  ;
{ TDatasetAdapter<T> }
{
****************************** TDatasetAdapter<T> ******************************
}
constructor TDatasetAdapter<T>.Create(ADataset: TDataset);
begin
  FDataset := ADataset;
  FIntf    := GetInterface;
end;
procedure TDatasetAdapter<T>.Cancel;
begin
  FDataset.Cancel;
end;
procedure TDatasetAdapter<T>.Close;
begin
  FDataset.Close;
end;
procedure TDatasetAdapter<T>.Delete;
begin
  FDataset.Delete;
end;
procedure TDatasetAdapter<T>.Edit;
begin
  FDataset.Edit;
end;
function TDatasetAdapter<T>.FieldByName(const FieldName: string): TField;
begin
  Result := FDataset.FieldByName(FieldName);
end;
procedure TDatasetAdapter<T>.First;
begin
  FDataset.First;
end;
function TDatasetAdapter<T>.GetCount: Integer;
begin
  Result := FDataset.RecordCount;
end;
function TDatasetAdapter<T>.GetCurrent: T;
begin
  Result := FIntf;
end;
function TDatasetAdapter<T>.GetEnumerator: IEnumerator<T>;
begin
  Reset;
  Result := Self;
end;
function TDatasetAdapter<T>.GetEof: Boolean;
begin
  Result := FDataset.Eof;
end;
function TDatasetAdapter<T>.GetInterface: T;
var
  LGuid: TGuid;
begin
  LGuid := GetTypeData(TypeInfo(T))^.Guid;
  if not Supports(Self, LGuid, Result) then
    Result := nil;
end;
procedure TDatasetAdapter<T>.Insert;
begin
  FDataset.Insert;
end;
procedure TDatasetAdapter<T>.Load;
begin
  Open;
  MapFields;
end;
procedure TDatasetAdapter<T>.MapFields;
begin
  //Stub procedure
end;
function TDatasetAdapter<T>.MoveNext: Boolean;
begin
  if FBof then FBof := False
  else         Next;
  Result := not Eof;
end;
procedure TDatasetAdapter<T>.Next;
begin
  FDataset.Next;
end;
procedure TDatasetAdapter<T>.Open;
begin
  FDataset.Open;
end;
procedure TDatasetAdapter<T>.Post;
begin
  FDataset.Post;
end;
procedure TDatasetAdapter<T>.Reset;
begin
  FBof := True;
  First;
end;
procedure TDatasetAdapter<T>.UnLoad;
begin
  Close;
end;
end.
You need to resolve function GetCurrent: T twice: for IDataList<T> and for Enumerator<T>. But you also need one for the non-generic ancestor of IEnumerator<T>: IEnumerator. Apparently that is not hidden by the GetCurrent method of IEnumerator<T>.
Try method resolution clauses:
function GetGenericCurrent: T; // implement this
function IDataList<T>.GetCurrent = GetGenericCurrent;
function IEnumerator<T>.GetCurrent = GetGenericCurrent;
function GetCurrent: TObject; // implement this -- can return nil.
The implementation of both can be the same, but you will have to make two methods. The one for the non-generic IEnumerator can return nil.
I had to modify the code above. Now it should work. It is not necessary to have two implementations for GetCurrent returning T, but you must have one returning TObject.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With