为什么 IEnumerable向集合添加元素后变空?

发布于 2024-11-19 06:05:01 字数 1159 浏览 6 评论 0原文

  • 我有一个 IEnumerable,当我迭代它并将其元素添加到列表中时,它会变成空吗?

  • 我对它的期望通常有什么问题吗 ?代码?

    苹果公共类
    {
        私人ICollection<水果> _fruits = new List();
    
        public void AddFruits(IEnumerablefruits)
        {
            if (fruits == null) throw new ArgumentNullException("fruits");
            foreach(水果中的水果)
            {
                _fruits.Add(水果);
            }
        }
    }
    

    调用者代码:

    public void AddFruits(IEnumerablefruitsToAdd)
    {
        foreach(苹果中的苹果)
        {
            // 这里fruitsToAdd 有元素,fruitsToAdd.ToList() 有两个水果。
            苹果.AddFruits(fruitsToAdd);
            // 这里的fruitsToAdd 没有元素!!,fruitsToAdd.ToList() 是空的!
            // 下一次迭代不会向下一个苹果添加任何水果,因为fruitsToAdd 为空。
        }
    }
    

更新

ToList() 解决了问题。问题的根源在于 AddFruits(IEnumerablefruitsToAdd) 的调用者发送了类似的fruitsToAdd。

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

每次 IEnumerablefruitsToAdd 为 Rest 时,它都会在上面的语句上运行。在下一次迭代中运行 except ,因此没有返回任何结果。

正确的方法是fruitsToAdd = obj.Fruits.Except(apples.Fruits).ToList();因为我们想要一个评估。

  • 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

凉城 2024-11-26 06:05:01

好吧,试试这个:

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 无法重复使用。其他人可以。

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.

过期情话 2024-11-26 06:05:01

我修改了你的代码以使其编译并编写了一个测试。将其元素复制到苹果中后,您的列表不会变空。

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

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);
            }
        }
    }
}
肩上的翅膀 2024-11-26 06:05:01

您问题中的代码不应表现出这种行为,因此我假设您尝试简化它,但从中删除了很多功能。

看起来有点可疑的是您的 _fruits 字段的类型为 ICollection。该接口通常与自定义集合实现一起使用。在实际代码中,该字段是否有可能不是使用 List 实例化的,而是使用该接口的自定义实现实例化的?

如果您有自定义集合实现,那么它的 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第一次迭代。

通过查看自定义集合的源代码,很难意识到 AddFruits 中的 _children.Add(f) 实际上修改了以前的父集合的fruits。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文