我有一个奇怪的事情,我正在做的一些代码同时修改了副本和原始列表。.我已尽最大努力将问题归结为仅在单个文件中显示错误。尽管以我的现实世界为例,我们要复杂得多。但是,从根本上讲,这就是问题所在。
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。原始代码对字符串进行了一些标记化,但是对于此演示,我简单地使用List.Add()对其进行伪造以使其易于阅读。
所以我现在有一个字符串和一个在每个元素中都有一个单词的列表。
这是我不理解的令人困惑的部分。我知道它与参考有关,但是我无法弄清楚如何适合它。
我有一种方法叫ModIt()...它很简单地接收一个列表,然后创建一个全新的列表,称为huh,将原始列表复制到新列表上,然后将其中的每一行更改为“ wtf? “。
现在,据我所知..我应该以3个变量结束...
1)字符串 2)每个元素中包含不同单词的列表 3)与其他元素长度相同的列表,每个元素均为“ wtf?”
但是,发生的是那是我尝试同时打印出两个都将每个元素都设置为“ WTF”的列表吗?我很困惑。我的意思是在ModIt中,我什至构建了一个全新的字符串,而不是修改正在传递的字符串,但这似乎没有任何作用。
这是输出...
:原始输入内容:这是猫的清单
:原始列表:这是猫的列表
**** Mod List Code已运行****
:原始输入内容:这是猫的清单
:原始列表:wtf? wtf? wtf? wtf? wtf? wtf?
:修改列表:wtf? wtf? wtf? wtf? wtf? wtf?
答案 0 :(得分:12)
huh = wordlist;
不会将wordlist
的项目复制到新列表中,而是将 reference 复制到wordlist
所占用的同一对象(即{ {1}}和huh
然后指向内存中的同一对象。
如果要复制,最简单的生产方法是使用LINQ:
wordlist
请注意,这将是“浅副本”。如果您的列表存储引用对象,则旧列表和新列表都将存储对相同对象的引用。
有关值与引用类型的更多信息,请参见here,如果需要深层副本,请参见here。
由于您要做的就是替换列表索引中的值,因此我认为可以使用浅表副本。
答案 1 :(得分:5)
约翰已经对错误代码发表了评论:
List<string> huh = new List<string>();
huh = wordlist;
您在这里创建了一个新列表,然后将其丢弃,并将引用huh
附加到旧列表中,所以呵呵和单词列表都指的是同一件事。
我只想指出复制列表的非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);
这就是我们所说的浅表副本
答案 2 :(得分:4)
您的问题来自以下事实:在C#中,我们具有引用类型和值类型。
值类型可以由直接赋值运算符(IClonable
)赋值,但对于引用类型则不同。引用类型并不存储实际的数据本身,而是在内存中存储数据的位置。就像指针一样,如果您来自C语言世界。
看看{{1}}。另请参阅Parameter passing by Jon Skeet,它对值和引用类型进行了很好的描述。