【问题标题】:How to uodate array inside a for loop and add it to a list如何更新for循环内的数组并将其添加到列表中
【发布时间】:2022-01-05 09:59:18
【问题描述】:

如果某个条件为真,我正在尝试更新数组并将其添加到列表中。正如您在我的代码中看到的那样,数组“rows”每次在 if 条件内都会更新,并将其添加到“checkList”中。

问题是,当我遍历列表以检查值时,似乎只有行的最后一个值已添加到列表中的每个条目中。

这里有一些代码来解释

        int[] rows = new int[2];
        List<int[]> checkList = new List<int[]>();

        for (int i = 0; i < 4; i++)
        {
            for (int j = 0; j < 4; j++)
            {
                if (true) 
                {

                    rows[0] = i;
                    rows[1] = j;

                    checkList.Add(rows);
                }
            }

        }

        foreach (var row in checkList)
        {
            Console.WriteLine(row[0] + "   " + row[1]);
        }

输出:

我希望有人能解释一下。谢谢

【问题讨论】:

    标签: c# arrays list


    【解决方案1】:

    .NET 中的大多数对象类型(包括数组)通过引用传递,因此checkList.Add(rows);对同一数组的引用多次添加到列表中。

    相反,您需要每次都创建一个新的数组实例:

    List<int[]> checkList = new List<int[]>();
    
    for (int i = 0; i < 4; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            if (true) 
            {
                checkList.Add(new int[]{ i, j });
            }
        }
    }
    

    【讨论】:

    • 谢谢,我只是想不通为什么我的代码不起作用,但这解决了问题:)
    【解决方案2】:

    我相信这里的问题是当你在

    checkList.Add(rows);
    

    您每次都向列表添加对行数组的引用,而不是它的单独副本。这会导致您当前的行为。

    解决方案是在循环内实例化数组,因此每次迭代都会创建一个新数组。

            List<int[]> checkList = new List<int[]>();
    
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    if (true) 
                    {
                        int[] rows = new int[2];
                        rows[0] = i;
                        rows[1] = j;
    
                        checkList.Add(rows);
                    }
                }
    
            }
    

    【讨论】:

      【解决方案3】:

      作为对 Matthias 答案的补充,关于 C# 可能不容易理解的一件事是,您拥有和使用的大多数变量只是对其他事物的引用。当你像这样分配一些变量时:

      int[] rows = new int[2];
      

      C# 在内存中创建一些空间来保存一个由 2 个整数组成的数组,它附加一个对它的引用,然后那个东西就成为你使用的变量,命名为 rows。如果你这样做:

      int[] rows2 = rows;
      

      它不会克隆使用的内存空间并创建一个新数组,它只是创建另一个附加到内存中相同数据的引用。如果数据是一只狗,现在它的项圈上有 2 条引线,但仍然只有一只狗。您可以拉动任何一条引线来敦促狗停止在汽车上撒尿,但它是您正在影响的同一只狗。

      数组/列表槽在这方面就像变量一样。说你有:

      List<int[]> checkList = new List<int[]>();
      

      意味着声明一个列表,其中每个槽都是能够引用 int 数组的变量。从概念上讲,这句话没有什么不同:

      int[] checkList0 = row;
      int[] checkList1 = row;
      int[] checkList2 = row;
      int[] checkList3 = row;
      

      只是这些数字被包含在名称中,而列表允许您以编程方式更改名称(并且具有 4 个以上的插槽):

      checkList[0] = row;
      checkList[1] = row;
      checkList[2] = row;
      checkList[3] = row;
      

      checkList[0] 在概念上是整个变量名,就像checkList0 是一个变量名一样,请记住,这只是另一个变量,它只是对内存中同一个数组的引用。

      通过不每次都创建new 数组,您将列表中的每个变量槽都附加到内存中的同一个数组,因此您最终在内存中得到了如下所示的内容:

      黑色是列表,蓝色是数组。每个列表槽只是对同一个数组的引用。您可能已将数组中的数字更改了 200 次,但在操作结束时,因为只有一个数组,您只能看到您写入数组的最后一组数字。您可能已将 20 根引线连接到您的狗身上,并且每根都拉了一次,但它仍然是同一只狗,被阻止了 20 次,无法在 20 辆汽车上撒尿。

      Matthias 的答案有效(并且应该这样做),因为您每次都具体地创建一个新数组

      蓝色数字是虚构的,并不代表您应该看到的答案;正在解释的概念是链接到内存中的新数组对象


      你会认为会被克隆是可以原谅的,因为 它是用于 intint 是值类型,表示使用时复制该值:

      int x = 1;
      int y = x;
      y = y + 1;
      

      y 现在是 2,但 x 仍然是 1。如果不是这样,即如果每次增加一些 int 变量,每隔一个变量,编写 C# 就会非常困难触及它所来自的变量的变量也受到了影响。所以我认为可能在本质上 合理 假设每当对 anything 进行赋值时,会影响分配变量的值不会影响它的早期迭代。但事实并非如此。值类型(分配时复制/克隆数据的类型)和引用类型(未复制/克隆数据的类型)之间存在明显的区别。 int 是值类型(克隆),int[] 是引用类型(未克隆)

      ..这是你真正需要记住并记住的事情


      滚动ref/out 是为了什么? 查询:D

      【讨论】:

      • 感谢您抽出宝贵的时间来写这个广泛的答案。这真的帮助我了解了幕后发生的事情。我真的很感激!
      猜你喜欢
      • 2015-11-28
      • 2020-03-12
      • 2021-07-01
      • 2020-10-22
      • 2017-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多