【问题标题】:Code is modifying the wrong variable... why?代码正在修改错误的变量...为什么?
【发布时间】:2019-06-01 19:55:50
【问题描述】:

我有一件奇怪的事情,我正在做的一些代码同时修改了副本和原始列表。我已经尽可能地将问题归结为仅在单个文件中显示错误。虽然我在现实世界中的例子要复杂得多.. 但归根结底,这一切都是问题所在。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestingRandomShit
{
    class Program
    {
        private static string rawInput;
        private static List<string> rawList;
        private static List<string> modifiedList;

        static void Main(string[] args)
        {
            rawInput = "this is a listing of cats";

            rawList = new List<string>();
            rawList.Add("this");
            rawList.Add("is");
            rawList.Add("a");
            rawList.Add("listing");
            rawList.Add("of");
            rawList.Add("cats");

            PrintAll();

            modifiedList = ModIt(rawList);

            Console.WriteLine("\n\n**** Mod List Code has been run **** \n\n");
            PrintAll();
        }

        public static List<string> ModIt(List<string> wordlist)
        {

            List<string> huh = new List<string>();
            huh = wordlist;

            for (int i = 0; i < huh.Count; i++)
            {
                huh[i] = "wtf?";
            }
            return huh;
        }

//****************************************************************************************************************
//Below is just a print function.. all the action is above this line


        public static void PrintAll()
        {
            Console.WriteLine(": Raw Input :");
            Console.WriteLine(rawInput);

            if (rawList != null)
            {
                Console.WriteLine("\n: Original List :");
                foreach (string line in rawList)
                {
                    Console.WriteLine(line);
                }
            }

            if (modifiedList != null)
            {
                Console.WriteLine("\n: Modified List :");
                foreach (string wtf in modifiedList)
                {
                    Console.WriteLine(wtf);
                }
                Console.ReadKey();
            }
        }
    }
}

基本上,我有三个变量....一个字符串和两个列表。原始代码对字符串进行了一些标记,但对于这个演示,我简单地使用 List.Add() 来伪造它以使其易于阅读。

所以我现在有一个字符串和一个列表,每个元素中都有一个单词。

这是我不明白的令人困惑的部分。我知道它与参考有关,但我不知道如何适应它。

我有一个方法叫做 ModIt()... 它简单地接受一个 List 然后创建一个名为 huh 的全新 List,将原始列表复制到新列表上,然后将 huh 中的每一行更改为“wtf? ”。

现在据我了解.. 我应该以 3 个变量结束...

1) 一个字符串 2) 每个元素中包含不同单词的 List 3)一个长度相同的列表,每个元素都是“wtf?”

但是,发生的情况是我尝试打印出两个列表,它们都将每个元素都设置为“WTF?”....所以是的.. wtf man?我超级困惑。我的意思是在 ModIt 中,我什至构建了一个全新的字符串,而不是修改正在传递的字符串,但它似乎没有任何影响。

这是输出...

:原始输入:这是猫的列表

:原始列表:这是猫的列表

**** 模组列表代码已运行 ****

:原始输入:这是猫的列表

:原始列表:wtf?什么?什么?什么?什么?什么鬼?

:修改列表:wtf?什么?什么?什么?什么?什么鬼?

【问题讨论】:

  • 这是因为huh = wordlist;你在做reference copy

标签: c#


【解决方案1】:

您的问题来自于在 C# 中我们有 reference typesvalue types

值类型可以通过直接赋值运算符 (=) 赋值,但对于引用类型则不同。引用类型本身不存储实际数据,它们在内存中存储数据所在的位置。就像指针一样,如果你来自 C 世界。

查看IClonable。另请阅读Parameter passing by Jon Skeet,它很好地描述了值和引用类型。

【讨论】:

    【解决方案2】:

    John 已经对错误代码发表了评论:

            List<string> huh = new List<string>();
            huh = wordlist;
    

    在这里您创建一个新列表,然后将其扔掉并将您的参考huh 附加到您的旧列表中,所以 huh 和 wordlist 指的是同一件事..

    我只是想指出复制列表的非 LINQ 方式:

            List<string> huh = new List<string>(wordlist);
    

    将旧列表传递给新列表的构造函数; list 有一个构造函数,它将一组对象存储在新列表中

    您现在有两个列表,最初它们都引用相同的字符串,但由于字符串无法更改,如果您开始更改列表中的字符串(而不是仅仅将它们从列表中洗牌或删除)新的将被创建

    如果是一个有价值的观点;您将有 2 个指向相同对象的列表,因此如果您将来有相同的场景,其中的对象可以更改,并且您在一个列表中更改对象,它也会在另一个列表中更改:

    //imagine the list stores people, the age of the first
    //person in the list is 27, and we increment it
    List1[0].PersonAge++;
    
    //list2 is a different list but refers to the same people objects
    //this will print 28
    Console.Out.WriteLine(list2[0].PersonAge);
    

    这就是我们所说的浅拷贝

    【讨论】:

      【解决方案3】:

      huh = wordlist; 不会将wordlist 的项目复制到新列表中,它会将引用 复制到wordlist 占用的同一对象(即huhwordlist然后指向内存中的同一个对象)。

      如果您想要一份副本,最简单的制作方法是使用 LINQ:

      List<string> huh = wordlist.ToList();
      

      请注意,这将是一个“浅拷贝”。如果您的列表存储引用对象,则旧列表和新列表都将存储对相同对象的引用。

      请参阅here 了解有关值与引用类型的更多信息,如果需要深拷贝,请参阅here

      由于您所做的只是替换列表索引处的值,我想浅拷贝就可以了。

      【讨论】:

      • 附带说明:由于 .NET 中字符串的不可变特性,这将按预期工作,但如果列表包含具有字符串属性的对象,它会带来一些有趣的结果,例如.
      • 谢谢大家.. 我不能接受答案,因为它发布得如此之快......但这有效,你给我的链接很有帮助。所以谢谢!
      猜你喜欢
      • 2017-01-08
      • 1970-01-01
      • 2021-12-11
      相关资源
      最近更新 更多