我正在尝试使用列表开发一种跟踪当前迭代位置的类型。
理想情况下,我希望使用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"));
}
}
答案 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.Select
和Where
的重载来实现大部分内容。
Select
和Where
都有重载,其中委托传递了项目及其索引:
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委托,但是这需要在产生该元素之前缓冲序列的元素以检测结束。