2009-06-29 4 views
1

Ich versuche, einen Typ zu entwickeln, um die aktuelle Iterationsposition mit einer Liste zu verfolgen.Geben Sie ein, um die Position einer Liste mit IEnumerable zu verfolgen

Ich möchte es idealerweise mit einer foreach-Schleife mit der IEnumerable-Schnittstelle verwenden, aber die Schnittstelle hat keine Start/Stop-Ereignisse oder Methode zum Hooking, um die Zählung zurückzusetzen.

Zur Zeit habe ich eine GetNext() Methode erstellt, die den nächsten Wert in der Liste aus und erhöht einen Zählwert um 1.

Rückkehr Kennt jemand ich die gleiche Funktionalität erreichen können IEnumerable verwenden, so kann ich den Typ verwenden mit eine foreach-Schleife?

Also zum Beispiel; Stellen Sie sich vor, eine Liste enthält 10 Elemente. Eine Methode könnte eine Instanz des Typs auf Position 4 iterieren, dann Methode 2 würde die gleiche Instanz beginnend an Position 5 bis 6 iterieren, dann Methode 3 würde die restng von Position 7 bis 10 iterieren - also die Typinstanz verfolgt die aktuelle Position.

Alle Ideen werden sehr geschätzt (Code unten gezeigt). Dank

public sealed class PositionTracker<T> : IEnumerable 
{ 
    private readonly object _syncLock = new object(); 
    private readonly IList<T> _list = new List<T>(); 
    private int _current; 

    public PositionTracker(IList<T> list) 
    { 
     _list = list; 
    } 

    public T GetCurrent() 
    { 
     lock (_syncLock) 
     { 
      return _list[_current]; 
     } 
    } 

    public T GetNext() 
    { 
     lock (_syncLock) 
     { 
      T t = GetCurrent(); 
      if (_current < _list.Count - 1) 
      { 
       _current++; 
      } 
      return t; 
     } 
    } 

    public IEnumerator<T> GetEnumerator() 
    { 
     lock (_syncLock) 
     { 
      return _list.GetEnumerator(); 
     } 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 

    public void Reset() 
    { 
     lock (_syncLock) 
     { 
      _current = 0; 
     } 
    } 

    public int Count 
    { 
     get 
     { 
      lock (_syncLock) 
      { 
       return _list.Count; 
      } 
     } 
    } 
} 

[TestFixture] 
public class PositionTrackerTests 
{ 
    [Test] 
    public void Position_CurrentPosition_Test() 
    { 
     List<string> list = new List<string>(new string[] { "A", "B", "C", "D" }); 
     PositionTracker<string> positionTracker = new PositionTracker<string>(list); 

     Assert.IsTrue(positionTracker.GetNext().Equals("A")); 
     Assert.IsTrue(positionTracker.GetNext().Equals("B")); 
     Assert.IsTrue(positionTracker.GetNext().Equals("C")); 
     Assert.IsTrue(positionTracker.GetNext().Equals("D")); 
    } 
} 

Antwort

1

Überprüfen Sie diesen Link tun: foreach with generic List, detecting first iteration when using value type

Es gibt einen Link zu einer SmartEnumerable Klasse von Jon Skeet. Es ist im Grunde ein Wrapper für IEnumerable, der Ihnen eine öffentliche Klasse SmartEnumerable<string>.Entry gibt, die den Index des Elements enthält.

Auch nichts hindert Sie daran, dies zu tun:

public class MyClass 
{ 
    private List<String> list = new List<String>() { "1", "2", "3", "4", "5" } 

    public IEnumerable<String> GetItems(int from, int to) 
    { 
      for (int i=from; i<to; i++) 
       yield return list[i]; 
    } 
} 
0

Sie können mit einer Erweiterungsmethode die meisten dies erreichen, und Überlastungen von Enumerable.Select und Where.

Beide Select und Where haben Überlastungen, wo die Delegierten sowohl das Element und seinen Index übergeben werden:

var input = new[]{'a','b','c','d'}; 
var indexed = input.Select((v,i) => new { Value = v, Index = i }); 
foreach (var v in indexed) { 
    Console.WriteLine("Index #{0} is '{1}'", v.Index, v.Value); 
} 

Delegierten auszulösen vor dem ersten und nach dem letzten Artikel (aber nur, wenn es mindestens ein Element):

public static IEnumerable<T> StartAndEnd<T>(this IEnumerable<T> input, 
            Action onFirst, 
            Action onLast) { 
    var e = input.GetEnumerator(); 
    if (!e.MoveNext()) { yield break; } 
    onFirst(); 
    do { 
    yield return e.Current; 
    } while (e.MoveNext()); 
    onLast(); 
} 

und dann verwenden, wie:

var input = new[]{'a','b','c','d'}; 
var indexed = input.StartAndEnd(() => { Console.WriteLine("First!");}, 
           () => { Console.WriteLine("Last!");}) 
        .Select((v,i) => new { Value = v, Index = i }); 
foreach (var v in indexed) { 
    Console.WriteLine("Index #{0} is '{1}'", v.Index, v.Value); 
} 

die das Ergebnis ergibt:

 
First! 
Index #0 is 'a' 
Index #1 is 'b' 
Index #2 is 'c' 
Index #3 is 'd' 
Last! 

Die Delegierten einen lokalen einstellen könnte (durch einen Verschluss bildet), die in einer Schleife überprüft wird.

Eine komplexere Version könnte den onLast-Delegaten vor dem letzten Element der Sequenz aufrufen, aber dafür müsste ein Element der Sequenz zwischengespeichert werden, um das Ende zu erkennen, bevor dieses Element zurückgegeben wird.

Verwandte Themen