[英]Does the GetEnumerator() in c# return a copy or the iterates the original source?
我有一个简单的GetEnumerator
用法。
private ConcurrentQueue<string> queue = new ConcurrentQueue<string>();
public IEnumerator GetEnumerator()
{
return queue.GetEnumerator();
}
我想更新这个 class 之外的队列。
所以,我在做:
var list = _queue.GetEnumerator();
while (list.MoveNext())
{
list.Current as string = "aaa";
}
GetEnumerator()
返回队列的副本,还是迭代原始值? 所以在更新的时候,我更新原来的?
谢谢:)
在迭代时修改集合(添加、删除、替换元素)通常是有风险的,因为人们不应该知道迭代器是如何实现的。
除此之外,还创建了一个队列以获取第一个元素/在末尾添加元素,但无论如何都不允许替换“中间”的元素。
以下是两种可行的方法:
迭代原始队列并在此过程中重新创建一个新集合。
var newQueueUpdated = new ConcurrentQueue<string>();
var iterator = _queue.GetEnumerator();
while (iterator.MoveNext())
{
newQueueUpdated.Add("aaa");
}
_queue = newQueueUpdated;
这自然是通过使用 linq .Select
在一个 go 中完成的,并将结果 IEnumerable 提供给 Queue 的构造函数:
_queue = new ConcurrentQueue<string>(_queue.Select(x => "aaa"));
当心,可能会消耗资源。 当然,其他实现也是可能的,尤其是当您的收藏很大时。
您可以使用包装器 class 来启用存储对象的突变:
public class MyObject
{
public string Value { get; set; }
}
然后你创建一个private ConcurrentQueue<MyObject> queue = new ConcurrentQueue<MyObject>();
反而。
现在您可以改变元素,而无需更改集合本身中的任何引用:
var enumerator = _queue.GetEnumerator();
while (enumerator.MoveNext())
{
enumerator.Current.Value = "aaa";
}
在上面的代码中,容器存储的引用从未改变。 不过,他们的内部 state已经改变了。
在问题代码中,您实际上试图将一个 object(字符串)更改为另一个 object,这在队列的情况下并不清楚,并且不能通过只读的.Current
来完成。 对于某些容器,它甚至应该被禁止。
下面是一些测试代码,看看我是否可以在迭代时修改ConcurrentQueue<string>
。
ConcurrentQueue<string> queue = new ConcurrentQueue<string>(new[] { "a", "b", "c" });
var e = queue.GetEnumerator();
while (e.MoveNext())
{
Console.Write(e.Current);
if (e.Current == "b")
{
queue.Enqueue("x");
}
}
e = queue.GetEnumerator(); //e.Reset(); is not supported
while (e.MoveNext())
{
Console.Write(e.Current);
}
成功运行并生成abcabcx
。
但是,如果我们将集合更改为标准的List<string>
那么它就会失败。
这是实现:
List<string> list = new List<string>(new[] { "a", "b", "c" });
var e = list.GetEnumerator();
while (e.MoveNext())
{
Console.Write(e.Current);
if (e.Current == "b")
{
list.Add("x");
}
}
e = list.GetEnumerator();
while (e.MoveNext())
{
Console.Write(e.Current);
}
在抛出InvalidOperationException
之前会产生ab
。
对于ConcurrentQueue
,这是由文档专门解决的:
枚举表示队列内容的即时快照。 它不反映调用 GetEnumerator 后对集合的任何更新。 枚举器可以安全地与队列的读取和写入并发使用。
所以答案是:它就像返回一个副本一样。 (它实际上并没有制作副本,但效果就好像它是一个副本 - 即在枚举它的同时更改原始集合不会更改枚举产生的项目。)
对于其他类型不保证此行为 - 例如,如果列表在枚举期间被修改,则尝试枚举List<T>
将失败。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.