con Delphi 2010, diciamo che ho una classe dichiarata come questo:modo migliore per implementare enumeratore filtrata su TList <TMyObject>
TMyList = TList<TMyObject>
Per questo elenco Delphi gentilmente ci fornisce un enumeratore, in modo che possiamo scrivere questo:
var L:TMyList;
E:TMyObject;
begin
for E in L do ;
end;
Il guaio è, vorrei scrivere questo:
var L:TMyList;
E:TMyObject;
begin
for E in L.GetEnumerator('123') do ;
end;
Cioè , Voglio la possibilità di fornire più enumeratori per la stessa lista, utilizzando alcuni criteri. Sfortunatamente l'implementazione di for X in Z
richiede la presenza di una funzione Z.GetEnumerator
, senza parametri, che restituisce l'enumeratore specificato! Per aggirare questo problema, sto definendo un'interfaccia che implementa la funzione "GetEnumerator", quindi implemento una classe che implementa l'interfaccia e infine scrivo una funzione su TMyList che restituisce l'interfaccia! E sto restituendo un'interfaccia perché non voglio essere disturbato a liberare manualmente la classe molto semplice ... In ogni caso, questo richiede MOLTA tipizzazione. Ecco come questo sarebbe simile:
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;
Usando questo posso finalmente scrivere:
var L:TMyList;
E:TMyObject;
begin
for E in L.FilteredEnum(7) do ;
end;
Conoscete un modo migliore di fare questo? Forse Delphi supporta un modo per chiamare direttamente GetEnumerator con un parametro?
Edit Più tardi:
ho deciso di utilizzare l'idea di Robert Love di attuare l'enumeratore utilizzando i metodi anonimi e l'utilizzo di fabbrica "record" di Gabr per salvare ancora un altra classe. Ciò mi consente di creare un enumeratore nuovo di zecca, completo di codice, utilizzando solo poche righe di codice in una funzione, senza richiedere una nuova dichiarazione di classe.
Ecco come il mio enumeratore generico viene dichiarato, in un'unità libreria:
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;
Ed ecco un modo per usarlo. Su qualsiasi classe che posso aggiungere una funzione come questa (e sto volutamente creare un enumeratore che non utilizza un List<T>
per mostrare la potenza di questo concetto):
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;
Ed ecco come userei questo nuovo enumeratore:
procedure Form1.Button1Click(Sender: TObject);
var N:Integer;
begin
for N in Numbers(3,10) do
Memo1.Lines.Add(IntToStr(N));
end;
Grazie per questo, ora l'implementazione del supporto dell'enumeratore per le classi è diventato molto più semplice. Se si desidera utilizzare qualcosa come 'per obj in list do', basta dichiarare GetEnumerator() in questo modo: 'function GetEnumerator: TEnumGenericAnonim' e nell'implementazione è sufficiente aggiungere '.GetEnumerator' alla fine dell'istruzione Create. –
Sharken