Используя Delphi 2010, допустим, у меня объявлен класс, подобный этому:
TMyList = TList<TMyObject>
Для этого списка Delphi любезно предоставит нам перечислитель, поэтому мы можем написать это:
var L:TMyList;
E:TMyObject;
begin
for E in L do ;
end;
Проблема в том, что я хотел бы написать так:
var L:TMyList;
E:TMyObject;
begin
for E in L.GetEnumerator('123') do ;
end;
То есть я хочу иметь возможность предоставлять несколько перечислителей для одного и того же списка, используя некоторые критерии.К сожалению, реализация for X in Z
требует наличия функции Z.GetEnumerator
без параметров, которая возвращает данный перечислитель!Чтобы обойти эту проблему, я определяю интерфейс, который реализует функцию «GetEnumerator», затем я реализую класс, который реализует интерфейс, и, наконец, я пишу функцию в TMyList, которая возвращает интерфейс!И я возвращаю интерфейс, потому что я не хочу возиться с ручным освобождением очень простого класса ... В любом случае, это требует МНОГО ввода.Вот как это будет выглядеть:
TMyList = class(TList<TMyObject>)
protected
// Simple enumerator; Gets access to the "root" list
TSimpleEnumerator = class
protected
public
constructor Create(aList:TList<TMyObject>; FilterValue:Integer);
function MoveNext:Boolean; // This is where filtering happens
property Current:TTipElement;
end;
// Interface that will create the TSimpleEnumerator. Want this
// to be an interface so it will free itself.
ISimpleEnumeratorFactory = interface
function GetEnumerator:TSimpleEnumerator;
end;
// Class that implements the ISimpleEnumeratorFactory
TSimpleEnumeratorFactory = class(TInterfacedObject, ISimpleEnumeratorFactory)
function GetEnumerator:TSimpleEnumerator;
end;
public
function FilteredEnum(X:Integer):ISimpleEnumeratorFactory;
end;
Используя это, я наконец могу написать:
var L:TMyList;
E:TMyObject;
begin
for E in L.FilteredEnum(7) do ;
end;
Знаете ли вы лучший способ сделать это?Может быть, Delphi поддерживает способ вызова GetEnumerator напрямую с параметром?
Позже Редактирование:
Я решил использовать идею Роберта Лава о реализации перечислителя с использованием анонимных методов ииспользуя "рекордную" фабрику Габра, чтобы сохранить еще один класс.Это позволяет мне создать новый перечислитель с кодом, используя всего несколько строк кода в функции, объявление нового класса не требуется.
Вот как объявляется мой универсальный перечислитель в модуле библиотеки:
TEnumGenericMoveNext<T> = reference to function: Boolean;
TEnumGenericCurrent<T> = reference to function: T;
TEnumGenericAnonim<T> = class
protected
FEnumGenericMoveNext:TEnumGenericMoveNext<T>;
FEnumGenericCurrent:TEnumGenericCurrent<T>;
function GetCurrent:T;
public
constructor Create(EnumGenericMoveNext:TEnumGenericMoveNext<T>; EnumGenericCurrent:TEnumGenericCurrent<T>);
function MoveNext:Boolean;
property Current:T read GetCurrent;
end;
TGenericAnonEnumFactory<T> = record
public
FEnumGenericMoveNext:TEnumGenericMoveNext<T>;
FEnumGenericCurrent:TEnumGenericCurrent<T>;
constructor Create(EnumGenericMoveNext:TEnumGenericMoveNext<T>; EnumGenericCurrent:TEnumGenericCurrent<T>);
function GetEnumerator:TEnumGenericAnonim<T>;
end;
И вот как это можно использовать.В любой класс я могу добавить функцию, подобную этой (и я намеренно создаю перечислитель, который не использует List<T>
, чтобы показать силу этой концепции):
type Form1 = class(TForm)
protected
function Numbers(From, To:Integer):TGenericAnonEnumFactory<Integer>;
end;
// This is all that's needed to implement an enumerator!
function Form1.Numbers(From, To:Integer):TGenericAnonEnumFactory<Integer>;
var Current:Integer;
begin
Current := From - 1;
Result := TGenericAnonEnumFactory<Integer>.Create(
// This is the MoveNext implementation
function :Boolean
begin
Inc(Current);
Result := Current <= To;
end
,
// This is the GetCurrent implementation
function :Integer
begin
Result := Current;
end
);
end;
И вот как яиспользовал бы этот новый перечислитель:
procedure Form1.Button1Click(Sender: TObject);
var N:Integer;
begin
for N in Numbers(3,10) do
Memo1.Lines.Add(IntToStr(N));
end;