Используя 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 с параметром напрямую?
Позднее Edit:
Я решил использовать идею Роберта Лава о внедрении счетчика, используя анонимные методы, и использовать gabr "record" factory, чтобы сохранить еще один класс. Это позволяет мне создать новый перечислитель с кодом, используя только несколько строк кода в функции, не требуется объявление нового класса.
Здесь, как объявлен мой общий счетчик в библиотечном блоке:
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 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;