2009-06-29 5 views
1

J'essaie de développer un type pour suivre la position d'itération actuelle avec une liste. Je souhaite idéalement l'utiliser avec une boucle foreach à l'aide de l'interface IEnumerable, mais l'interface ne possède pas d'événements de démarrage/arrêt ou de méthode d'accrochage pour réinitialiser le comptage.Tapez pour suivre la position d'une liste à l'aide de IEnumerable

méthode Actuellement, j'ai créé un GetNext() qui retourne la valeur suivante dans la liste et incrémente un comptage par 1.

Est-ce que quelqu'un sait que je peux obtenir la même fonctionnalité en utilisant IEnumerable je peux utiliser le type avec une boucle foreach?

Ainsi, par exemple; Imaginez une liste contient 10 éléments. Une méthode pourrait itérer une instance du type en position 4, puis la méthode deux itérerait la même instance à partir de la position 5 à 6, puis la méthode 3 itérerait la position restante de la position 7 à 10 - donc l'instance de type suit la position actuelle.

Toutes les idées sont grandement appréciées (code ci-dessous). Merci

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")); 
    } 
} 

Répondre

1

Vérifiez ce lien: foreach with generic List, detecting first iteration when using value type

Il y a un lien vers une classe SmartEnumerable par Jon Skeet. Il s'agit essentiellement d'un wrapper pour IEnumerable, qui vous donne une classe publique SmartEnumerable<string>.Entry qui contient l'index de l'élément.

De plus, rien ne vous empêche de le faire:

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

Vous pouvez obtenir plus de cela avec une méthode d'extension et de Enumerable.Select et surcharges Where.

Les deux Select et Where ont une surcharge où le délégué est passé à la fois l'élément et son indice:

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); 
} 

Pour déclencher les délégués avant la première et après les derniers éléments (mais seulement si au moins un élément):

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(); 
} 

puis l'utiliser comme:

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); 
} 

qui donne le résultat:

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

Les délégués pourraient définir un local (par formation d'une fermeture) qui est contrôlé en boucle.

Une version plus sophistiquée pourrait appeler le délégué onLast avant le dernier élément de la séquence, mais cela nécessiterait de mettre en mémoire tampon un élément de la séquence pour détecter la fin avant de fournir cet élément.

Questions connexes