Как реализовать IEnumerable<T>
?
Задний план
Допустим, у меня есть класс, который я хочу реализовать IEnumerable<T>
:
TStackoverflow<T> = class(TInterfacedObject, IEnumerable<T>)
public
{ IEnumerable<T> }
function GetEnumerator: IEnumerator<T>;
end;
var
IEnumerable<TMouse> mices = TStackoverflow<TMouse>.Create;
у меня была бы реализация:
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;
Теперь, чтобы быть хорошим программистом, я выберу, чтобы мой класс также поддерживал интерфейс IEnumerable
:
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;
Те из вас, кто знает, к чему все идет, поймут, что внедрение IEnumerable
— отвлекающий маневр; и нужно было сделать так или иначе.
Теперь возникает проблема
Этот код не компилируется, потому что:
function GetEnumerator: IEnumerator<T>;
function GetEnumerator: IEnumerator;
у меня есть два метода с одинаковой сигнатурой (на самом деле не с одной и той же сигнатурой, но достаточно одинаковой, чтобы Delphi не мог их различить):
E2254 Перегруженная процедура GetEnumerator должна быть помечена директивой overload.
Хорошо, хорошо, я отмечу их как overloads
:
function GetEnumerator: IEnumerator<T>; overload;
function GetEnumerator: IEnumerator; overload;
Но это не работает:
E2252 Метод GetEnumerator с идентичными параметрами уже существует
Разрешение метода интерфейса
Перегрузка была неправильным подходом, нам следует обратить внимание на предложения разрешения методов :
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;
За исключением того, что это тоже не компилируется по причинам, которые ускользают от меня:
E2291 Отсутствует реализация метода интерфейса IEnumerable.GetEnumerator.
Так что давай забудем IEnumerable
Притворитесь, что мне все равно, если мой класс не поддерживает IEnumerable
, давайте удалим его как поддерживаемый интерфейс. На самом деле это ничего не меняет, так как IEnumerable<T>
происходит от IEnumerble
:
IEnumerable<T> = interface(IEnumerable)
function GetEnumerator: IEnumerator<T>;
end;
поэтому метод должен существовать, а код, удаляющий 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;
не компилируется по той же причине:
E2291 Отсутствует реализация метода интерфейса IEnumerable.GetEnumerator.
Хорошо, тогда забудьте об дженериках
Итак, давайте остановимся, посотрудничаем и послушаем. IEnumerable вернулся как старое новое изобретение:
type
TStackoverflow = class(TInterfacedObject, IEnumerable)
public
function GetEnumerator: IEnumerator;
end;
{ TStackoverflow }
function TStackoverflow.GetEnumerator: IEnumerator;
begin
end;
Отлично, это работает. Я могу получить поддержку своего класса IEnumerable
. Теперь я хочу поддержать IEnumerable<T>
.
Что приводит меня к моему вопросу:
Обновление: забыл прикрепить полный нефункциональный код:
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>
на идентификаторTStackoverflowCollection<T>
, посколькуIEnumerable
обычно применяется для коллекций и объектов, состоящих из других объектов. - person umlcat   schedule 24.10.2014IEnumerable<T>
в C#. Тем более сyield return
сейчас последнее делается буквально за минуты. Задать вопрос в одиночку — это в 10 раз больше времени, не говоря уже о времени, потраченном на то, чтобы задать этот вопрос на SO. (Да, я знаю, чтоyield return
может подойти не во всех случаях, аyield return
тогда не было). - person JensG   schedule 08.03.2017Build All
, и компилятор не может найти класс, который вы создаете. СделайтеBuild All
еще раз, и это сработает. Компилятор Delphi попахивает малофункциональным, собранным воедино, запутанным беспорядком. Я бы хотел, чтобы в C# была такая же мощная библиотека виджетов, как в Delphi. - person Ian Boyd   schedule 09.03.2017