简体   繁体   English

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

[英]Why IEnumerable<T> becomes empty after adding elements to a collection?

  • I have an IEnumerable<T> when I iterate through it and add it's element to a list it becomes empty?我有一个IEnumerable<T>当我遍历它并将它的元素添加到它变成空的列表中?

  • 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. ToList() 解决了这个问题。 The root of the problem was that the caller to AddFruits(IEnumerable fruitsToAdd) send fruitsToAdd that was like.问题的根源是 AddFruits(IEnumerable fruitsToAdd) 的调用者发送了 fruitsToAdd 就像这样。

fruitsToAdd = obj.Fruits.Except(apples.Fruits); fruitsToAdd = obj.Fruits.Except(apples.Fruits);

Each time IEnumerable fruitsToAdd was Rest it run above statement.每次 IEnumerable fruitsToAdd 是 Rest 它运行上面的语句。 Which at next iteration run Except and thereby returned no fruits.在下一次迭代运行时除外,因此没有返回任何结果。

The right way is fruitsToAdd = obj.Fruits.Except(apples.Fruits).ToList();正确的方法是 fruitsToAdd = obj.Fruits.Except(apples.Fruits).ToList(); Since we want one evaluation.因为我们想要一个评估。

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.如果不知道您的fruitsToAdd的来源,就不可能多说。 Some IEnumerable<> can't be re-used.某些IEnumerable<>不能重复使用。 Others can.其他人可以。

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> .看起来有点可疑的是您的_fruits字段的类型是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?是否有可能,在实际代码中,该字段不是使用List<T>实例化的,而是使用该接口的自定义实现?

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").如果你有一个自定义集合实现,那么它的Add方法完全有可能做一些奇怪的事情(比如在将它添加到新的“父”集合之前从其先前的“父”集合中删除一个项目)。 Tree collections often do such things to simplify moving nodes around.树 collections 经常做这样的事情来简化移动节点。

[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.我知道这不是 OP 的实际问题,但我仍然会添加一个示例来演示自定义集合实现实际上可以在其成员添加到不同集合时修改输入集合。

Let's say the Fruit class looks like this:假设Fruit class 看起来像这样:

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.因为apple.GetFruits()在第一次迭代后会返回 0。

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.通过查看自定义集合的来源,很难意识到AddFruits中的_children.Add(f)实际上修改了 fruits 先前的父集合。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM