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