代码之家  ›  专栏  ›  技术社区  ›  Aydin Homay

使用列表中的take方法后的Removerange方法有意外行为

c#
  •  1
  • Aydin Homay  · 技术社区  · 6 年前

    有人知道为什么下面代码中离开while循环体后的长度是零吗?请不要给我提供分块算法我已经知道几种这样的算法,我知道如何解决这个问题。我的问题只是关于移动范围或可能采取的有线行为。

    static void Main(string[] args)
    {
    
        var list = new List<int>();
        for (int i = 0; i < 1000; i++)
        {
            list.Add(i);
        }
    
        var chunks = new List<IEnumerable<int>>();
    
        while (list.Count > 0)
        {
            chunks.Add(list.Take(10));
            list.RemoveRange(0, 10);
        }
    
        int length = chunks.ToList()[0].Count();
    }
    
    4 回复  |  直到 6 年前
        1
  •  3
  •   Christos    6 年前

    var chunks = new List<IEnumerable<int>>();
    

    你创建了一个列表 Count 为0,因为列表中没有任何项。然后在 foreach 在您添加的每一步中 list.Take(10) 在这之后,你要从 list 重要的 上榜(10) (更多的时候你会听到这个词 ). 第一次评估的时间如下:

    int length = chunks.ToList()[0].Count();
    

    在这条线上 上榜(10) ,中没有任何元素 . 因此, 返回一个空序列,因此 chunks.ToList()[0]

    更新 :

    我们有以下清单:

    var list = new List<int> {1, 2, 3, 4, 5 };
    var firstThree = list.Take(3);
    

    firstThree 保存对枚举数的引用—特别是 IEnumerable<int> . 它不包含数组或1,2,3的列表 列表

    比如你可以打电话给 ToList ToArray 方法:

    var firstThreeList = firstThree.ToList();
    var firstThreeArray = firstThree.ToArray();
    

    列表 你会拿到前三件东西。

    这就是说,很明显,如果同时你修改了 列表 把所有的数字从 iist ,就不会有任何元素 列表 你什么也得不到。

    作为一个测试,我建议您运行上面的代码一次,然后再运行一次 之前 托利斯特 ToArray公司

    list.RemoveAll(x => true);
    

    现在你会注意到 firstThreeArray firstThreeList 都是空的。

        2
  •  1
  •   adjan    6 年前

    chunks.Add(list.Take(10));
    

    实际上并没有从 list ,它只告诉你当 chunks[i] Count() ). 因为您正在更改列表,所以引用指向一个空列表。您可以使用

    chunks.Add(list.Take(10).ToList());
    
        3
  •  1
  •   ProgrammingLlama Raveena Sarda    6 年前

    你遇到的问题是LINQ有点像一个视图。它实际上只在调用需要它生成特定值的方法时才遍历集合( First() Last() , Count() 等)。因此,只在调用其中一个方法时计算列表。

    chunks.Add(list.Take(10));
    

    list ,并且当有人重复你的时候,只做前10项”。要解决此问题,可以将该小部分转换为列表(计算这10个项目,并从中创建新列表):

    chunks.Add(list.Take(10).ToList());
    

    List<string> names = new List<string>() { "a", "b", "c" };
    IEnumerable<string> skip2 = names.Skip(2);
    Console.WriteLine(string.Join(", ", skip2)); // "c"
    names.Add("d");
    names.Add("e");
    Console.WriteLine(string.Join(", ", skip2)); // "c, d, e"
    

    IEnumerable<string> skip2 string.Join(", ", skip2) 它每次都会遍历列表,即使列表已更改。

    因此,你将得到 "c" 在第一次跑步时 "c, d, e" 在第二轮。

    事实上,这是完全正确的(尽管更难理解):

    List<int> list = new List<int>();
    IEnumerable<int> values = list.DefaultIfEmpty(0);
    list.Add(5);
    list.Add(10);
    list.Add(15);
    Console.WriteLine(values.Average()); // 10
    
        4
  •  0
  •   Hasan Emrah Süngü    6 年前

    根本没有奇怪的行为。这正是我们的预期行为 IEnumerable . 你应该记住的是 是延迟求值的,这意味着它在枚举时求值,即当您实际访问 I可数 . 你所做的基本上是

    list 对象
    准备学习上述课程的前10个要素 列表 对象, 不要评估!
    not yet evaluated (LINQ.Take(10)) 进入之内 chunks 列表。
    列表
    列表
    创建一个列表,全部由 尚未评估 list.Take(10) .
    你把第一个元素 大块 ,即 not yet evaluate 但它指的是 列表 ,这是空的!!!
    §你打电话来 Count 在IEnumerable实例上,它最终计算enmpy的前十个元素 列表