当它可以被修改时经历一个foreach?
我想在取出foreach循环的成员时执行foreach循环,但这会抛出错误。 我唯一的想法是在这个循环中创建另一个列表以找到要删除的切片,并循环遍历新列表以从Pizza中删除项目。
foreach(var Slice in Pizza) { if(Slice.Flavor == "Sausage") { Me.Eat(Slice); //This removes an item from the list: "Pizza" } }
你可以做到这一点,到目前为止我发现的最简单的方法(想想我发明了它,确定这不是真的;))
foreach (var Slice in Pizza.ToArray()) { if (Slice.Flavor == "Sausage") // each to their own.. would have gone for BBQ { Me.Eat(Slice); } }
因为它正在迭代循环的固定副本。 它将迭代所有项目,即使它们被删除。
得心应手不是!
(顺便说一句,这是一种迭代集合副本的方便方法,具有线程安全性,并删除对象被锁定的时间:锁定,获取ToArray()副本,释放锁定,然后迭代)
希望有所帮助!
如果您必须遍历列表并需要删除项目,请使用for循环向后迭代:
// taken from Preet Sangha's answer and modified for(int i = Pizza.Count-1; i >= 0, i--) { var Slice = Pizza[i]; if(Slice.Flavor == "Sausage") { Me.Eat(Slice); //This removes an item from the list: "Pizza" } }
向后迭代的原因是当你删除Elements时,你不会遇到因在我们删除了第六个元素的Pizza上访问Pizza [5]而导致的IndexOutOfRangeException。
使用for循环的原因是因为迭代器变量i与Pizza无关,所以你可以在没有枚举器“破坏”的情况下修改Pizza
使用for循环而不是foreach
for(int i = 0; i < in Pizza.Count(), ++i) { var Slice = Pizza[i]; if(Slice.Flavor == "Sausage") { Me.Eat(Slice); //This removes an item from the list: "Pizza" } }
接近这个的最明确的方法是建立一个吃片的列表,然后处理它,避免在循环中改变原始枚举。 我从来不喜欢使用索引循环,因为它可能容易出错。
List slicesToEat=new List (); foreach(var Slice in Pizza) { if(Slice.Flavor == "Sausage") { slicesToEat.Add(Slice); } } foreach(var slice in slicesToEat) { Me.Eat(slice); }
也许更改你的Me.Eat()
签名以获取IEnumerable
Me.Eat(Pizza.Where(s=>s.Flavor=="Sausage").ToList());
这使您可以在一行代码中执行任务。
然后你的Eat()
就像:
public void Eat(IEnumerable remove) { foreach (Slice r in remove) { Pizza.Remove(r); } }
VB6样式的“Collection”对象允许在枚举期间进行修改,并且在发生此类修改时似乎能够合理地工作。 太糟糕了它有其他限制(键类型仅限于不区分大小写的字符串)并且不支持generics,因为其他集合类型都不允许修改。
坦率地说,我不清楚为什么微软的iEnumerable合同要求在修改集合时抛出exception。 我理解一个要求,如果对集合的更改使得枚举无法继续而没有古怪(跳过或复制枚举,崩溃等期间未更改的值),则会抛出exception但是没有理由不这样做允许一个可以合理列举的集合。
你在哪里可以订购披萨,其中切片有独立的浇头? 无论如何…
使用Linq:
// Was "Me.Eat()" supposed to be "this.Eat()"? Pizza .Where(slice => slice.Flavor == "Sausage") .Foreach(sausageSlice => { Me.Eat(sausageSlice); });
前两行创建一个仅包含香肠切片的新列表。 第三个将采用该新子集并将每个切片传递给Me.Eat()。 {和;}可能是多余的。 这不是最有效的方法,因为它首先制作副本(与已经给出的许多其他方法一样),但它肯定是干净和可读的。
顺便说一句,这只适用于后人,因为已经给出了最好的答案 – 按索引向后迭代。
比萨什么样的系列? 如果它是List RemoveAll
方法:
Pizza.RemoveAll(slice => string.Equals(slice.Flavor, "Sausage"));