键入以使用IEnumerable跟踪列表的位置

时间:2009-06-29 09:29:40

标签: c#

我正在尝试使用列表开发一种跟踪当前迭代位置的类型。

理想情况下,我希望使用IEnumerable接口将它与foreach循环一起使用,但接口没有启动/停止事件或方法来挂钩以重置计数。

目前我已经创建了一个GetNext()方法,该方法返回列表中的下一个值并将计数递增1。

有谁知道我可以使用IEnumerable实现相同的功能所以我可以使用带有foreach循环的类型吗?

所以例如;想象一个列表包含10个项目。一种方法可以将类型的实例迭代到位置4,然后方法二将迭代从位置5到6开始的相同实例,然后方法3将余数从位置7迭代到10 - 因此类型实例跟踪当前位置。

非常感谢任何想法(代码如下所示)。感谢

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

3 个答案:

答案 0 :(得分:2)

查看yield关键字。 Especially this link of Chapter 6 of the Book 'C# in Depth' By Jon Skeet

P.S。我希望你是用C#.NET 2.0 +

做的

答案 1 :(得分:1)

点击此链接:foreach with generic List, detecting first iteration when using value type

Jon Skeet提供了SmartEnumerable课程的链接。它基本上是IEnumerable的包装器,它为您提供了一个包含项目索引的公共SmartEnumerable<string>.Entry类。

此外,没有什么能阻止你这样做:

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

答案 2 :(得分:0)

您可以使用扩展方法以及Enumerable.SelectWhere的重载来实现大部分内容。

SelectWhere都有重载,其中委托传递了项目及其索引:

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

在第一个项目之前和最后一个项目之后触发代理(但仅当至少有一个项目时):

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

然后将其用作:

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

给出了结果:

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

代理人可以设置一个本地(通过形成一个闭包),这是一个循环检查。

更复杂的版本可以在序列的最后一个元素之前调用onLast委托,但是这需要在产生该元素之前缓冲序列的元素以检测结束。