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を直接呼び出す方法をサポートしていますか?
後で編集:
私は、匿名メソッドを使用して列挙子を実装し、gabrの「レコード」ファクトリを使用してさらに他のクラスを保存するというRobertLoveのアイデアを使用することにしました。これにより、関数で数行のコードを使用するだけで、新しいクラス宣言を必要とせずに、コードを備えたまったく新しい列挙子を作成できます。
ライブラリユニットで、私の汎用列挙子がどのように宣言されるかを次に示します。
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;