我有一个IEnumerable<T>
当我迭代它并将它的元素添加到列表中它变为空?
我对代码的期望通常有什么不妥吗?
public class Apple
{
private ICollection<Fruit> _fruits = new List<Fruit>();
public void AddFruits(IEnumerable<Fruit> fruits)
{
if (fruits == null) throw new ArgumentNullException("fruits");
foreach (var fruit in fruits)
{
_fruits.Add(fruit);
}
}
}
来电者代码:
public void AddFruits(IEnumerable<Fruit> fruitsToAdd)
{
foreach (var apple in apples)
{
// Here fruitsToAdd has elements, fruitsToAdd.ToList() has two fruits.
apple.AddFruits(fruitsToAdd);
// Here fruitsToAdd has NO element!!, fruitsToAdd.ToList() is empty!
// next iteration will not add any fruit to next apple since fruitsToAdd is empty.
}
}
更新
ToList()解决了这个问题。问题的根源是AddFruits(IEnumerable fruitsToAdd)的调用者发送了像FruitToAdd那样的。
fruitsToAdd = obj.Fruits.Except(apples.Fruits);
每次IEnumerable fruitsToAdd都是Rest它运行上面的语句。在下一次迭代中运行除外,从而没有返回结果。
正确的方法是fruitsToAdd = obj.Fruits.Except(apples.Fruits).ToList();因为我们想要一次评估。
答案 0 :(得分:3)
好的,试试这个:
public void AddFruits(IEnumerable<Fruit> fruitsToAdd)
{
var fruitsToAddCopy = fruitsToAdd.ToList(); // add just this line
foreach (var apple in apples)
{
apple.AddFruits(fruitsToAddCopy); // and change this
}
}
如果不知道fruitsToAdd
的来源,就不可能多说。某些IEnumerable<>
无法重复使用。其他人可以。
答案 1 :(得分:0)
你问题中的代码不应该表现出这样的行为,所以我假设你试图简化它,但从中删除了很多功能。
有些可疑的是,您的_fruits
字段属于ICollection<T>
类型。此接口通常与自定义集合实现一起使用。是否有可能在实际代码中,此字段未使用List<T>
进行实例化,而是使用该接口的自定义实现?
如果你有一个自定义集合实现,那么它的Add
方法完全可以做奇怪的事情(比如在将它添加到新的“父”之前从之前的“父”集合中删除一个项目) 。树集合经常做这些事情来简化周围的移动节点。
<强> [编辑] 强>
我知道这不是OP的实际问题,但我会添加一个示例来证明自定义集合实现 实际上可以在将其成员添加到其他成员时修改输入集合集合。
假设Fruit
类看起来像这样:
partial class Fruit
{
private ICollection<Fruit> _children;
private Fruit _parent;
public String Name { get; set; }
public Fruit()
{
_children = new FruitCollection(this);
}
public void AddFruits(IEnumerable<Fruit> fruits)
{
foreach (Fruit f in fruits)
_children.Add(f);
}
public int NumberOfChildren
{
get { return _children.Count; }
}
public IEnumerable<Fruit> GetFruits()
{
return _children.ToList();
}
}
并且有一个自定义集合定义为:
partial class Fruit
{
public class FruitCollection : Collection<Fruit>
{
private readonly Fruit _parent;
public FruitCollection(Fruit parent)
{
_parent = parent;
}
protected override void InsertItem(int index, Fruit item)
{
// item already has a parent?
if (item._parent != null)
{
// remove it from previous parent
item._parent._children.Remove(item);
}
// set the new parent
item._parent = _parent;
base.InsertItem(index, item);
}
// other methods should be overriden in a similar way
}
}
然后是以下程序:
static void Main(string[] args)
{
List<Fruit> abc = new List<Fruit>()
{
new Fruit() { Name = "a" },
new Fruit() { Name = "b" },
new Fruit() { Name = "c" }
};
Fruit apple = new Fruit() { Name = "apple" };
apple.AddFruits(abc);
Console.WriteLine("{0} has {1} children", apple.Name, apple.NumberOfChildren);
// now try to add apples's children to
// each of the following fruits
List<Fruit> def = new List<Fruit>()
{
new Fruit() { Name = "d" },
new Fruit() { Name = "e" },
new Fruit() { Name = "f" }
};
foreach (Fruit f in def)
{
f.AddFruits(apple.GetFruits());
Console.WriteLine("{0} has {1} children", f.Name, f.NumberOfChildren);
}
Console.Read();
}
会打印:
apple has 3 children d has 3 children e has 0 children f has 0 children
因为apple.GetFruits()
将在第一次迭代后返回0。
通过查看自定义集合的源代码,很难意识到_children.Add(f)
中的AddFruits
实际上修改了前一个父集合中的水果。
答案 2 :(得分:0)
我修改了你的代码,让它编译并编写了一个测试。将它的元素复制到苹果中后,您的列表不会变空。
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace ClassLibrary3
{
[TestClass]
public class Class1
{
[TestMethod]
public void test()
{
var fruits = new List<Fruit> {new Fruit(), new Fruit(), new Fruit()};
var lists = AddFruits(fruits);
Assert.IsTrue(fruits.Count == 3);
}
public List<Apple> AddFruits(IEnumerable<Fruit> fruitsToAdd)
{
var apples = new List<Apple>
{
new Apple(),
new Apple()
};
foreach (var apple in apples)
{
apple.AddFruits(fruitsToAdd);
}
return apples;
}
}
public class Fruit
{
}
public class Apple
{
private ICollection<Fruit> _fruits = new List<Fruit>();
public void AddFruits(IEnumerable<Fruit> fruits)
{
if (fruits == null) throw new ArgumentNullException("fruits");
foreach (var fruit in fruits)
{
_fruits.Add(fruit);
}
}
}
}