How do i implement IEnumerable<T>
?
Background
Lets say i have a class that i want to implement IEnumerable<T>
:
TStackoverflow<T> = class(TInterfacedObject, IEnumerable<T>)
public
{ IEnumerable<T> }
function GetEnumerator: IEnumerator<T>;
end;
var
IEnumerable<TMouse> mices = TStackoverflow<TMouse>.Create;
i would have an implementation:
TStackoverflow<T> = class(TInterfacedObject, IEnumerable<T>)
public
{ IEnumerable<T> }
function GetEnumerator: IEnumerator<T>;
end;
function TStackoverflow<T>.GetEnumerator: IEnumerator<T>;
begin
Result := {snip, not important here};
end;
Now, in order to be a good programmer, i will choose that my class will also support the IEnumerable
interface:
TStackoverflow<T> = class(TInterfacedObject, IEnumerable<T>, IEnumerable)
public
{ IEnumerable<T> }
function GetEnumerator: IEnumerator<T>;
{ IEnumerable }
function GetEnumerator: IEnumerator;
end;
function TStackoverflow<T>.GetEnumerator: IEnumerator<T>;
begin
Result := {snip, not important here};
end;
function TStackoverflow.GetEnumerator: IEnumerator;
begin
Result := {snip, not important here};
end;
Those of you who know where this is going, will know that implementing IEnumerable
is a red-herring; and had to be done either way.
Now comes the problem
That code doesn't compile, because:
function GetEnumerator: IEnumerator<T>;
function GetEnumerator: IEnumerator;
i have two methods with the same signature (not really the same signature, but same enough that Delphi can't distinguish between them):
E2254 Overloaded procedure 'GetEnumerator' must be marked with the 'overload' directive
Ok, fine, i'll mark them as overloads
:
function GetEnumerator: IEnumerator<T>; overload;
function GetEnumerator: IEnumerator; overload;
But that doesn't work:
E2252 Method 'GetEnumerator' with identical parameters already exists
Interface method resolution
Overloading was the wrong approach, we should be looking to method resolution clauses:
type
TStackoverflow<T> = class(TInterfacedObject, IEnumerable<T>, IEnumerable)
protected
function GetEnumeratorTyped: IEnumerator<T>;
function GetEnumeratorGeneric: IEnumerator;
public
function IEnumerable<T>.GetEnumerator = GetEnumeratorTyped;
function IEnumerable.GetEnumerator = GetEnumeratorGeneric;
end;
{ TStackoverflow }
function TStackoverflow<T>.GetEnumeratorGeneric: IEnumerator;
begin
end;
function TStackoverflow<T>.GetEnumeratorTyped: IEnumerator<T>;
begin
end;
Except this doesn't compile either, for reasons that escape me:
E2291 Missing implementation of interface method IEnumerable.GetEnumerator
So lets forget IEnumerable
Pretend i don't care if my class doesn't support IEnumerable
, lets remove it as a supported interface. It doesn't actually change anything, as IEnumerable<T>
descends from IEnumerble
:
IEnumerable<T> = interface(IEnumerable)
function GetEnumerator: IEnumerator<T>;
end;
so the method must exist, and the code that removes IEnumerable
:
type
TStackoverflow<T> = class(TInterfacedObject, IEnumerable<T>)
protected
function GetEnumeratorTyped: IEnumerator<T>;
public
function IEnumerable<T>.GetEnumerator = GetEnumeratorTyped;
end;
{ TStackoverflow }
function TStackoverflow<T>.GetEnumeratorTyped: IEnumerator<T>;
begin
end;
doesn't compile for the same reason:
E2291 Missing implementation of interface method IEnumerable.GetEnumerator
Alright then, forget generics
So lets stop, collaborate and listen. IEnumerable is back as an old new invention:
type
TStackoverflow = class(TInterfacedObject, IEnumerable)
public
function GetEnumerator: IEnumerator;
end;
{ TStackoverflow }
function TStackoverflow.GetEnumerator: IEnumerator;
begin
end;
Excellent, that works. I can have my class support IEnumerable
. Now i want to support IEnumerable<T>
.
Which leads me to my question:
Update: Forgot to attach complete non-functional code:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
TStackoverflow<T> = class(TInterfacedObject, IEnumerable<T>, IEnumerable)
protected
function GetEnumeratorTyped: IEnumerator<T>;
function GetEnumeratorGeneric: IEnumerator;
public
function IEnumerable<T>.GetEnumerator = GetEnumeratorTyped;
function IEnumerable.GetEnumerator = GetEnumeratorGeneric;
end;
{ TStackoverflow<T> }
function TStackoverflow<T>.GetEnumeratorGeneric: IEnumerator;
begin
end;
function TStackoverflow<T>.GetEnumeratorTyped: IEnumerator<T>;
begin
end;
begin
try
{ TODO -oUser -cConsole Main : Insert code here }
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
TStackoverflow<T>
identifier for aTStackoverflowCollection<T>
identifier, sinceIEnumerable
usually applied for collections & objects composed by others objects. – TedmannIEnumerable<T>
in C#. Especially withyield return
now the latter is done literally in minutes. To ask the question alone is 10x that time, let alone the time spent before asking that question on SO. (Yes, I am aware thatyield return
might not be suitable in all cases and there was noyield return
back then). – BenadrylBuild All
and the compiler can't find the class you're constructing. DoBuild All
again and it works. The Delphi compiler reeks of a barely functional, cobbled together, tangled, mess. I wish C# had the same powerful widget library that Delphi does. – Palladium