Iterate/enumerate 超过列表的一部分?
Iterate/enumerate over part of a list?
有没有办法记住枚举数的位置?
我想记住枚举的位置,以便我可以将它重置为当前位置之前的位置。我不想回到开始,所以 .reset() 没有帮助。
顺便说一句,是否可以让枚举器从 2. 位置开始?
List<string> list = new List<string>(new string[] { "a", "b", "c" });
IEnumerator<string> i = list.GetEnumerator();
i.MoveNext(); richTextBoxOutput.AppendText(i.Current);
IEnumerator<string> t = i; // how do I make a real copy i?
i.MoveNext(); richTextBoxOutput.AppendText(i.Current);
i = t;
i.MoveNext(); richTextBoxOutput.AppendText(i.Current);
您确定需要 IEnumerator 实例吗?为什么不枚举使用索引并将其存储在您自己的变量中?
var list = new List<string>(new { "a", "b", "c" });
var pos = 2; // this is the position
richTextBoxOutput.AppendText(list[pos]);
您可以随时重置:
pos = (desired position);
因为你已经有一个 List<>
为什么不维护一个 indexer/counter 然后使用 IEnumerable Skip() extension method (and possibly combine that with Take() followed by ForEach()).
一些可能有用的进一步信息:
- MSDN: Return Or Skip Elements in a Sequence
- Stack Overflow: LINQ with Skip and Take
Is there a way to remember the position of an enumerator?
有时。这取决于枚举器的实现方式。
在这种情况下,枚举器被实现为一个可变的 struct
,这是一种性能优化,人们更经常 运行 在它产生这种 "freeze position" 行为的情况下不想要它。 (如果你曾经写过一个通用的 class 来包装 IEnumerable<T>
的实现,那么要么将该引用作为接口类型而不是类型本身,要么就不要它 readonly
即使它看起来应该是这样,但如果你这样做了,你最终可能会永久冻结这样一个结构枚举器。
只需更改您的代码,而不是:
IEnumerator<string> i = list.GetEnumerator();
…
IEnumerator<string> t = i;
你有:
List<string>.Enumerator i = list.GetEnumerator();
…
List<string>.Enumerator t = i;
或者简单地说:
var i = list.GetEnumerator();
…
var t = i;
现在您根据此 struct
定义了 i
和 t
并且从一个复制到另一个复制了 struct
而不仅仅是对盒装 struct
.
这不适用于所有枚举器,就此而言,在编写您自己的枚举器时故意使其可用并不是最好的方法(如果您需要这样做,您最好添加某种Clone()
或 Snapshot()
枚举器的方法是 class
而不是 struct
),但它适用于 List<T>
.
一个不依赖于这种怪癖的实现的更灵活的解决方案是:
public class SnapshotableListEnumerator<T> : IEnumerator<T>
{
private readonly IList<T> _list;
private int _idx;
private SnapshotableListEnumerator(IList<T> list, int idx)
{
_list = list;
_idx = idx;
}
public SnapshotableListEnumerator(IList<T> list)
: this(list, -1)
{
}
public bool MoveNext()
{
// Note that this enumerator doesn't complain about the list
// changing during enumeration, but we do want to check that
// a change doesn't push us past the end of the list, rather
// than caching the size.
if(_idx >= _list.Count)
return false;
++_idx;
return true;
}
public void Reset()
{
_idx = -1;
}
public T Current
{
get
{
if(_idx < 0 || _idx >= _list.Count)
throw new InvalidOperationException();
return _list[_idx];
}
}
object IEnumerator.Current
{
get { return Current; }
}
public void Dispose()
{
}
public SnapshotableListEnumerator<T> Snapshot()
{
return new SnapshotableListEnumerator<T>(_list, _idx);
}
}
public static class SnapshotableListEnumeratorHelper
{
public static SnapshotableListEnumerator<T> GetSnapshotableEnumerator<T>(this IList<T> list)
{
return new SnapshotableListEnumerator<T>(list);
}
}
现在您可以在 IList<T>
的任何实现上调用 GetSnapshotableEnumerator()
并在您需要枚举中位置的副本时使用其 Snapshot()
方法。
有没有办法记住枚举数的位置? 我想记住枚举的位置,以便我可以将它重置为当前位置之前的位置。我不想回到开始,所以 .reset() 没有帮助。 顺便说一句,是否可以让枚举器从 2. 位置开始?
List<string> list = new List<string>(new string[] { "a", "b", "c" });
IEnumerator<string> i = list.GetEnumerator();
i.MoveNext(); richTextBoxOutput.AppendText(i.Current);
IEnumerator<string> t = i; // how do I make a real copy i?
i.MoveNext(); richTextBoxOutput.AppendText(i.Current);
i = t;
i.MoveNext(); richTextBoxOutput.AppendText(i.Current);
您确定需要 IEnumerator 实例吗?为什么不枚举使用索引并将其存储在您自己的变量中?
var list = new List<string>(new { "a", "b", "c" });
var pos = 2; // this is the position
richTextBoxOutput.AppendText(list[pos]);
您可以随时重置:
pos = (desired position);
因为你已经有一个 List<>
为什么不维护一个 indexer/counter 然后使用 IEnumerable Skip() extension method (and possibly combine that with Take() followed by ForEach()).
一些可能有用的进一步信息:
- MSDN: Return Or Skip Elements in a Sequence
- Stack Overflow: LINQ with Skip and Take
Is there a way to remember the position of an enumerator?
有时。这取决于枚举器的实现方式。
在这种情况下,枚举器被实现为一个可变的 struct
,这是一种性能优化,人们更经常 运行 在它产生这种 "freeze position" 行为的情况下不想要它。 (如果你曾经写过一个通用的 class 来包装 IEnumerable<T>
的实现,那么要么将该引用作为接口类型而不是类型本身,要么就不要它 readonly
即使它看起来应该是这样,但如果你这样做了,你最终可能会永久冻结这样一个结构枚举器。
只需更改您的代码,而不是:
IEnumerator<string> i = list.GetEnumerator();
…
IEnumerator<string> t = i;
你有:
List<string>.Enumerator i = list.GetEnumerator();
…
List<string>.Enumerator t = i;
或者简单地说:
var i = list.GetEnumerator();
…
var t = i;
现在您根据此 struct
定义了 i
和 t
并且从一个复制到另一个复制了 struct
而不仅仅是对盒装 struct
.
这不适用于所有枚举器,就此而言,在编写您自己的枚举器时故意使其可用并不是最好的方法(如果您需要这样做,您最好添加某种Clone()
或 Snapshot()
枚举器的方法是 class
而不是 struct
),但它适用于 List<T>
.
一个不依赖于这种怪癖的实现的更灵活的解决方案是:
public class SnapshotableListEnumerator<T> : IEnumerator<T>
{
private readonly IList<T> _list;
private int _idx;
private SnapshotableListEnumerator(IList<T> list, int idx)
{
_list = list;
_idx = idx;
}
public SnapshotableListEnumerator(IList<T> list)
: this(list, -1)
{
}
public bool MoveNext()
{
// Note that this enumerator doesn't complain about the list
// changing during enumeration, but we do want to check that
// a change doesn't push us past the end of the list, rather
// than caching the size.
if(_idx >= _list.Count)
return false;
++_idx;
return true;
}
public void Reset()
{
_idx = -1;
}
public T Current
{
get
{
if(_idx < 0 || _idx >= _list.Count)
throw new InvalidOperationException();
return _list[_idx];
}
}
object IEnumerator.Current
{
get { return Current; }
}
public void Dispose()
{
}
public SnapshotableListEnumerator<T> Snapshot()
{
return new SnapshotableListEnumerator<T>(_list, _idx);
}
}
public static class SnapshotableListEnumeratorHelper
{
public static SnapshotableListEnumerator<T> GetSnapshotableEnumerator<T>(this IList<T> list)
{
return new SnapshotableListEnumerator<T>(list);
}
}
现在您可以在 IList<T>
的任何实现上调用 GetSnapshotableEnumerator()
并在您需要枚举中位置的副本时使用其 Snapshot()
方法。