Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compiler not mapping a class method to an interface method

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.
like image 987
Terry Thompson Avatar asked Oct 27 '25 07:10

Terry Thompson


1 Answers

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.


Update

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.

like image 97
Rudy Velthuis Avatar answered Oct 29 '25 08:10

Rudy Velthuis



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!