Reputation: 5086
I have an IEnumerable<T>
when I iterate through it and add it's element to a list it becomes empty?
Is there generally anything wrong about what I expect from the code?
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);
}
}
}
The caller code:
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.
}
}
Update
The ToList() solved the problem. The root of the problem was that the caller to AddFruits(IEnumerable fruitsToAdd) send fruitsToAdd that was like.
fruitsToAdd = obj.Fruits.Except(apples.Fruits);
Each time IEnumerable fruitsToAdd was Rest it run above statement. Which at next iteration run Except and thereby returned no fruits.
The right way is fruitsToAdd = obj.Fruits.Except(apples.Fruits).ToList(); Since we want one evaluation.
Upvotes: 1
Views: 1985
Reputation: 51214
The code in your question shouldn't exhibit such behavior, so I am presuming you tried to simplify it, but removed a lot of functionality from it.
What looks a bit suspicious is that your _fruits
field is of type ICollection<T>
. This interface is often used with custom collection implementations. Is it possible that, in the actual code, this field isn't instantiated with a List<T>
, but rather with a custom implementation of that interface?
If you have a custom collection implementation, then it is perfectly possible for its Add
method to do weird stuff (like removing an item from its previous "parent" collection before adding it to its new "parent"). Tree collections often do such things to simplify moving nodes around.
[Edit]
I am aware that this is not OPs actual problem, but I will nevertheless add an example to demonstrate that a custom collection implementation can in fact modify the input collection when its members are added to a different collection.
Let's say the Fruit
class looks like this:
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();
}
}
And there is a custom collection defined as:
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
}
}
Then the following program:
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();
}
Would print:
apple has 3 children d has 3 children e has 0 children f has 0 children
Because apple.GetFruits()
will return 0 after the first iteration.
By looking at the custom collection's source, it is hard to realize that _children.Add(f)
in AddFruits
in fact modifies the fruits previous parent collection.
Upvotes: 0
Reputation: 273219
Ok, try this:
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
}
}
Without knowing the origin of your fruitsToAdd
it's impossible to say more. Some IEnumerable<>
can't be re-used. Others can.
Upvotes: 3
Reputation: 1547
I modified your code to get it to compile and wrote a test. Your list does not become empty after copying it's elements into the apples.
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);
}
}
}
}
Upvotes: 0