【问题标题】:Both c# lists being modified两个 c# 列表都被修改
【发布时间】:2016-03-11 12:26:08
【问题描述】:

我的两个列表都在被修改,并试图避免这种行为......

这里有什么问题?

我有一个“大”列表,我想删除 itemsToRemoveList 中存在的所有项目,但不让它修改原始列表。

我已经简化了示例代码..

List<string> aList = new List<string>(){"My","name","is", "jeff"}; // cached full list

List<string> bList = aList; // initial copy

List<string> itemsToRemoveList = new List<string>(){"jeff"};

bList.RemoveAll(itemsToRemoveList.Contains); // remove only from copy

foreach (string s in aList)
{
    Console.Write(s + " "); // expect "my name is jeff"
}
Console.WriteLine();
foreach (string s in bList)
{
    Console.Write(s + " "); // expect "my name is"
}
// however this modifies both collections. 

        Console.ReadLine();

【问题讨论】:

  • 这不是一个“副本”,它只是创建了对同一个列表的另一个引用。
  • 如果您的列表是值类型或引用类型,这是一个非常有用的链接,它涵盖了最好的方法:stackoverflow.com/questions/222598/…
  • @sr28 如果项目被克隆或另一个列表的副本,则处理创建一个新列表。这里不需要,因为string 是不可变的。

标签: c# data-structures


【解决方案1】:

当你这样做时

List<string> bList = aList;

这不是创建新列表。它只是将您的 bList 变量设置为指向与 aList 相同的列表的引用。制作副本的方法是实际创建一个新列表。

List<string> bList = new List<string>(aList);

但如果您还想过滤值,最好使用 Linq。

List<string> aList = new List<string>(){"My","name","is", "jeff"}; 
List<string> itemsToRemoveList = new List<string>(){"jeff"};
List<string> bList = aList.Where(a => !itemsToRemoveList.Contains(a)).ToList(); 

foreach (string s in aList)
{
    Console.Write(s + " "); 
}
Console.WriteLine();
foreach (string s in bList)
{
    Console.Write(s + " "); 
}

【讨论】:

    【解决方案2】:

    请记住,.Net 中的对象是引用类型,当您将一种引用类型分配给另一种引用类型时,您实际上是在分配一个指向原始对象内存位置的指针。

    List<string> aList = new List<string>(){"My","name","is", "jeff"}; // cached full list
    List<string> bList = aList; // <- NOT a copy, this is assigning the same object
    

    您提到您有一个相当大的列表。如果您的意思是数千或更少,那么朱哈尔的回答很棒。如果您的列表中有数百万个项目,您可能需要重新考虑最初是如何填充该列表的,并且可能需要修改您的数据库查询。

    【讨论】:

      【解决方案3】:

      您可以使用 LINQ 除外:

      List<int> a = new List<int>{ 1, 2 };
      List<int> b = new List<int> { 2 };
      var c = a.Except(b);
      

      【讨论】:

        【解决方案4】:

        您出现这种行为是因为您只是在引用您的原始列表。您的第二个列表只是指向与原始列表相同的内存地址。您需要添加如下内容:

            List<string> aList = new List<string>() { "My", "name", "is", "jeff" }; // cached full list
        
            List<string> bList = new List<string>();
        
            foreach(var item in aList)
            {
                bList.add(item);
            }
        
            List<string> itemsToRemoveList = new List<string>() { "jeff" };
        
            bList.RemoveAll(itemsToRemoveList.Contains); // remove only from copy
        
            foreach (string s in aList)
            {
                Console.Write(s + " "); // expect "my name is jeff"
            }
                Console.WriteLine();
            foreach (string s in bList)
            {
                Console.Write(s + " "); // expect "my name is"
            }
            // however this modifies both collections. 
        
            Console.ReadLine();
        

        这将创建原始列表的真实副本并完成您正在寻找的行为。

        【讨论】:

          【解决方案5】:

          在您的代码中:

          List<string> bList = aList;
          

          不复制列表,而是复制对列表的引用。这就是为什么当您更改一个列表时,更改会反映在另一个列表中,因为它们实际上是同一个列表。

          要创建一个新列表,您需要使用:

          List<string> bList = new List<string>(aList);
          

          有关详细信息,请参阅List 参考。

          还要确保您了解 C# 中值类型和引用类型之间的区别。当您分配引用类型时,您只是在复制引用,而当您分配值类型时,您会复制数据。只有原始类型、结构和枚举是 C# 中的值类型,其他一切都是引用类型。在您的示例中,您正在处理一个作为引用类型的列表对象,但您希望它的行为类似于值类型。阅读更多here

          【讨论】:

            【解决方案6】:

            你的问题是你只是在同一个对象上引用。 为避免这种行为,您必须像这样初始化列表 B:

            List<string> bList = new List<string>(aList);
            

            【讨论】:

              猜你喜欢
              • 2020-06-15
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-05-29
              • 1970-01-01
              • 2013-12-16
              相关资源
              最近更新 更多