Note that there are some explanatory texts on larger screens.

plurals
  1. POBetter way to implement filtered enumerator on TList<TMyObject>
    text
    copied!<p>Using Delphi 2010, let's say I've got a class declared like this:</p> <pre><code>TMyList = TList&lt;TMyObject&gt; </code></pre> <p>For this list Delphi kindly provides us with an enumerator, so we can write this:</p> <pre><code>var L:TMyList; E:TMyObject; begin for E in L do ; end; </code></pre> <p>The trouble is, I'd like to write this:</p> <pre><code>var L:TMyList; E:TMyObject; begin for E in L.GetEnumerator('123') do ; end; </code></pre> <p>That is, I want the ability to provide multiple enumerators for the same list, using some criteria. Unfortunately the implementation of <code>for X in Z</code> requires the presence of a function <code>Z.GetEnumerator</code>, with no parameters, that returns the given enumerator! To circumvent this problem I'm defining an interface that implements the "GetEnumerator" function, then I implement a class that implements the interface and finally I write a function on TMyList that returns the interface! And I'm returning an interface because I don't want to be bothered with manually freeing the very simple class... Any way, this requires a LOT of typing. Here's how this would look like:</p> <pre><code>TMyList = class(TList&lt;TMyObject&gt;) protected // Simple enumerator; Gets access to the "root" list TSimpleEnumerator = class protected public constructor Create(aList:TList&lt;TMyObject&gt;; 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; </code></pre> <p>Using this I can finally write:</p> <pre><code>var L:TMyList; E:TMyObject; begin for E in L.FilteredEnum(7) do ; end; </code></pre> <p>Do you know a better way of doing this? Maybe Delphi does support a way of calling GetEnumerator with a parameter directly?</p> <p><strong>Later Edit:</strong></p> <p>I decided to use Robert Love's idea of implementing the enumerator using anonymous methods and using gabr's "record" factory to save yet an other class. This allows me to create a brand new enumerator, complete with code, using just a few lines of code in a function, no new class declaration required.</p> <p>Here's how my generic enumerator is declared, in a library unit:</p> <pre><code>TEnumGenericMoveNext&lt;T&gt; = reference to function: Boolean; TEnumGenericCurrent&lt;T&gt; = reference to function: T; TEnumGenericAnonim&lt;T&gt; = class protected FEnumGenericMoveNext:TEnumGenericMoveNext&lt;T&gt;; FEnumGenericCurrent:TEnumGenericCurrent&lt;T&gt;; function GetCurrent:T; public constructor Create(EnumGenericMoveNext:TEnumGenericMoveNext&lt;T&gt;; EnumGenericCurrent:TEnumGenericCurrent&lt;T&gt;); function MoveNext:Boolean; property Current:T read GetCurrent; end; TGenericAnonEnumFactory&lt;T&gt; = record public FEnumGenericMoveNext:TEnumGenericMoveNext&lt;T&gt;; FEnumGenericCurrent:TEnumGenericCurrent&lt;T&gt;; constructor Create(EnumGenericMoveNext:TEnumGenericMoveNext&lt;T&gt;; EnumGenericCurrent:TEnumGenericCurrent&lt;T&gt;); function GetEnumerator:TEnumGenericAnonim&lt;T&gt;; end; </code></pre> <p>And here's a way to use it. On any class I can add a function like this (and I'm intentionally creating an enumerator that doesn't use a <code>List&lt;T&gt;</code> to show the power of this concept):</p> <pre><code>type Form1 = class(TForm) protected function Numbers(From, To:Integer):TGenericAnonEnumFactory&lt;Integer&gt;; end; // This is all that's needed to implement an enumerator! function Form1.Numbers(From, To:Integer):TGenericAnonEnumFactory&lt;Integer&gt;; var Current:Integer; begin Current := From - 1; Result := TGenericAnonEnumFactory&lt;Integer&gt;.Create( // This is the MoveNext implementation function :Boolean begin Inc(Current); Result := Current &lt;= To; end , // This is the GetCurrent implementation function :Integer begin Result := Current; end ); end; </code></pre> <p>And here's how I'd use this new enumerator:</p> <pre><code>procedure Form1.Button1Click(Sender: TObject); var N:Integer; begin for N in Numbers(3,10) do Memo1.Lines.Add(IntToStr(N)); end; </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload