2011-05-18 20 views
9

TDictionary'den türetilmiş bir koleksiyon tanımladım ve ek bir filtre uygulayan özel bir numaralayıcı tanımlamanız gerekiyor.TDictionary türetilmiş bir sınıf için özel bir numaralayıcı nasıl oluşturulur?

I (bu özel olan) TDictionary FItems dizi erişemez şaşırıp yüzden

nasıl elde edilen bir sınıfı, bir, süzüldü numaralandırıcıyı yeniden tanımlamak için ilerleyecekti MoveNext yöntemini tanımlamak olamaz TDictionary?

TMyItem = class(TObject) 
public 
    IsHidden:Boolean; // The enumerator should not return hidden items 
end; 
TMyCollection<T:TMyItem> = class(TDictionary<integer,T>) 
public 
    function GetEnumerator:TMyEnumerator<T>; // A value filtered enumerator 
    type 
    TMyEnumerator = class(TEnumerator<T>) 
    private 
     FDictionary: TMyCollection<integer,T>; 
     FIndex: Integer; 
     function GetCurrent: T; 
    protected 
     function DoGetCurrent: T; override; 
     function DoMoveNext: Boolean; override; 
    public 
     constructor Create(ADictionary: TMyCollection<integer,T>); 
     property Current: T read GetCurrent; 
     function MoveNext: Boolean; 
    end; 
end; 

function TMyCollection<T>.TMyEnumerator.MoveNext: Boolean; 
begin 
// In below code, FIndex is not accessible, so I can't move forward until my filter applies 
    while FIndex < Length(FDictionary.FItems) - 1 do 
    begin 
    Inc(FIndex); 
    if (FDictionary.FItems[FIndex].HashCode <> 0) 
     and not(FDictionary.FItems[FIndex].IsHidden) then // my filter 
     Exit(True); 
    end; 
    Result := False; 
end; 

cevap

5

Numaralandırıcınızı TDictionary numaralandırıcısına dayandırabilirsiniz, böylece FItems adresine erişiminiz gerekmez. Bu, Barry'nin önerdiği gibi TDictionary etrafında bir sarmalayıcı sınıf yazsanız bile çalışır. listeleyicisi şu şekilde görünecektir:

program Project23; 

{$APPTYPE CONSOLE} 

uses 
    SysUtils, Generics.Collections; 

type 

    TMyType = class 
    public 
    Int: Integer; 
    constructor Create(anInteger:Integer); 
    end; 

    TMyCollection<T:TMyType> = class(TDictionary<integer,T>) 
    strict private 
    type 
     TMyEnumerator = class 
     protected 
     BaseEnum: TEnumerator<TPair<Integer,T>>; 
     function GetCurrent: T; 
     public 
     constructor Create(aBaseEnum: TEnumerator<TPair<Integer,T>>); 
     destructor Destroy;override; 

     function MoveNext:Boolean; 
     property Current:T read GetCurrent; 
     end; 
    public 
    function GetEnumerator: TMyEnumerator; 
    end; 

{ TMyCollection<T> } 

function TMyCollection<T>.GetEnumerator: TMyEnumerator; 
begin 
    Result := TMyEnumerator.Create(inherited GetEnumerator); 
end; 

{ TMyType } 

constructor TMyType.Create(anInteger: Integer); 
begin 
    Int := anInteger; 
end; 

{ TMyCollection<T>.TMyEnumerator } 

constructor TMyCollection<T>.TMyEnumerator.Create(aBaseEnum: TEnumerator<TPair<Integer, T>>); 
begin 
    BaseEnum := aBaseEnum; 
end; 

function TMyCollection<T>.TMyEnumerator.GetCurrent: T; 
begin 
    Result := BaseEnum.Current.Value; 
end; 

destructor TMyCollection<T>.TMyEnumerator.Destroy; 
begin 
    BaseEnum.Free; 
    inherited; 
end; 

function TMyCollection<T>.TMyEnumerator.MoveNext:Boolean; 
begin 
    Result := BaseEnum.MoveNext; 
    while Result and ((BaseEnum.Current.Value.Int mod 2) = 1) do 
    Result := BaseEnum.MoveNext; 
end; 

var TMC: TMyCollection<TMyTYpe>; 
    V: TMyType; 

begin 
    try 
    TMC := TMyCollection<TMyType>.Create; 
    try 
     // Fill TMC with some values 
     TMC.Add(1, TMyType.Create(1)); 
     TMC.Add(2, TMyType.Create(2)); 
     TMC.Add(3, TMyType.Create(3)); 
     TMC.Add(4, TMyType.Create(4)); 
     TMC.Add(5, TMyType.Create(5)); 
     TMC.Add(6, TMyType.Create(6)); 
     TMC.Add(7, TMyType.Create(7)); 
     TMC.Add(8, TMyType.Create(8)); 
     // Filtered-enum 
     for V in TMC do 
     WriteLn(V.Int); 
     ReadLn; 
    finally TMC.Free; 
    end; 
    except 
    on E: Exception do 
     Writeln(E.ClassName, ': ', E.Message); 
    end; 
end. 
+0

Bu tam örnek için şimdi sayımcıların nasıl kullanılacağına dair daha iyi bir anlayışa sahibim. Ayrıca, sarmalayıcıların büyük bir performans cezasına sahip olduğunu da keşfettim (örnek uygulamamda, bir nesne sorgusu, herhangi bir filtre uygulamadan, sarılı numaralayıcı ile normal TDictionary sayımcısı, 3.3 ms (+% 50!) Ile 2.2 ms alır). – user315561

+0

@ user315561, 2.2ms nedir? Modern bir işlemci üzerinde 2.2 msn çok uzun bir süre, muhtemelen bir şey hesapladıysanız; Ve bu, sarılmış numaralayıcı için 3.3 ms sonucunu eşit olarak yanlış yapar. –

+0

İçeriğim dışında verilen rakamların uygun olmadığını kabul ediyorum. Kullanım durumu, basit bir eşitlik filtresi ile sıralı olarak taranan ve yeni bir sonuç koleksiyonuna eklenen 850 eşleşen nesne olan 75000 nesneden oluşan bir koleksiyon üzerinde bir sorgulamadır. Burada vurgulamak istediğim, sayımla alakalı olan ve iyi bilinen numaralayıcı sargısının (aynı kullanım durumunda) maliyet cezasıdır. – user315561

4

Sen TDictionary sarar ziyade doğrudan ondan devralan bir sınıf yazmalısınız:

İşte yapmak istediğim göstermek için basit bir kod. TDictionary'un tek nedeni, TObjectDictionary'un tanımlanabilmesi ve onunla polimorfik kalmasıdır. Yani, TDictionary'u geçersiz kılarak yalnızca uygun destek, anahtarlar ve değerler sözlükten kaldırıldığında ne olacağını özelleştirmek (böylece serbest bırakılması gerekebilir).

+1

Teşekkür Barry, iyi öneri: Burada

TMyEnumerator = class protected BaseEnumerator: TEnumerator<TPair<Integer, T>>; // using the key and value you used in your sample public function MoveNext:Boolean; property Current:T read GetCurrent; end; function TMyEnumerator.MoveNext:Boolean; begin Result := BaseEnumerator.MoveNext; while Result and (not (YourTestHere)) do // ie: the base enumerator returns everything, reject stuff you don't like Result := BaseEnumerator.MoveNext; end; function TMyEnumerator.Current: T; begin Result := BaseEnumerator.Current.Value; // Based on your example, it's value you want to extract end; 

Ve bu gösteriyor komple, 100 satır konsol uygulaması var. Diğer avantaj, koleksiyonumun sözlük seçimine daha az eşleşmesini sağlamaktır. Bu, daha yüksek bir performans dict ile değiştirilebilmesini kolaylaştırır. – user315561

İlgili konular