将元素添加到集合后为什么IEnumerable <t>变空?</t>

时间:2011-07-10 21:08:00

标签: c# linq generics ienumerable

  • 我有一个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();因为我们想要一次评估。

3 个答案:

答案 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);
            }
        }
    }
}