如何在不更改原始列表的情况下更改新列表?

我有一个列表,从操作中填入一些数据,我将其存储在内存缓存中。 现在我想要另一个列表,其中包含基于某些条件的列表中的一些子数据。

从下面的代码中可以看出,我正在对目标列表进行一些操作。 问题是我对目标列表所做的任何更改也都在对mainList进行。 我认为它因为参考是相同的或什么的。

我只需要在目标列表上的操作不会影响主列表中的数据。

List target = mainList; SomeOperationFunction(target); void List SomeOperationFunction(List target) { target.removeat(3); return target; } 

您需要在方法中克隆列表,因为List是一个类,所以它是引用类型并通过引用传递。

例如:

 List SomeOperationFunction(List target) { List tmp = target.ToList(); tmp.RemoveAt(3); return tmp; } 

要么

 List SomeOperationFunction(List target) { List tmp = new List(target); tmp.RemoveAt(3); return tmp; } 

要么

 List SomeOperationFunction(List target) { List tmp = new List(); tmp.AddRange(target); tmp.RemoveAt(3); return tmp; } 

您需要复制列表,以便对副本的更改不会影响原件。 最简单的方法是在System.Linq使用ToList扩展方法。

 var newList = SomeOperationFunction(target.ToList()); 

首先构建一个新列表并对其进行操作,因为List是一个引用类型,即当你在函数中传递它时,你不仅要传递值而是传递实际对象本身。

如果只是将target指定给mainList ,则两个变量都指向同一个对象,因此您需要创建一个新的List:

 List target = new List(mainList); 

void List SomeOperationFunction()没有意义,因为要么返回任何内容( void ),要么返回List 。 因此,要么从方法中删除return语句,要么返回一个新的List 。 在后一种情况下,我会将其重写为:

 List target = SomeOperationFunction(mainList); List SomeOperationFunction(List target) { var newList = new List(target); newList.RemoveAt(3); return newList; } 

您的目标变量是引用类型。 这意味着您对它所做的任何事情都会反映在您传递给它的列表中。

若要不这样做,您将需要在方法中创建新列表,将target内容复制到该列表,然后在新列表上执行删除操作。

关于参考和价值类型

即使您创建了一个新列表,对新列表中项目的引用仍将指向旧列表中的项目,因此如果我需要一个包含新引用的新列表,我希望使用此扩展方法…

 public static IEnumerable Clone(this IEnumerable target) where T : ICloneable { If (target.IsNull()) throw new ArgumentException(); List retVal = new List(); foreach (T currentItem in target) retVal.Add((T)(currentItem.Clone())); return retVal.AsEnumerable(); } 

您正在查看正在修改的原始列表,因为默认情况下,任何非基本对象都是通过引用传递的(它实际上是按值传递,值是引用,但这是另一回事)。

你需要做的是克隆对象。 这个问题将帮助您使用一些代码克隆C#中的List: 如何在C#中克隆通用列表?

而不是将mainList分配给目标,我会这样做: target.AddRange(mainList);

然后,您将获得项目的副本,而不是对列表的引用。

只需确保使用通过复制源列表的元素创建的列表初始化新列表。

List target = mainList; 应该是List target = new List(mainList);

您需要复制一份列表,因为在原始代码中您正在做的只是传递,正如您所怀疑的那样,一个引用(某人称之为指针)。

您可以在新列表上调用构造函数,将原始列表作为参数传递:

 List SomeOperationFunction(List target) { List result = new List(target); result.removeat(3); return result; } 

或者创建一个MemberWiseClone :

 List SomeOperationFunction(List target) { List result = target.MemberWiseClone(); result.removeat(3); return result; } 

此外,您没有在任何地方存储SomeOperationFunction的返回值,因此您可能也想修改该部分(您将该方法声明为void ,它不应返回任何内容,但在其中您将返回一个对象)。 你应该这样调用方法:

 List target = SomeOperationFunction(mainList); 

注意:列表的元素不会被复制(只复制它们的引用),因此修改元素的内部状态将影响两个列表。

由于List是引用类型,因此传递给函数的是对原始列表的引用。

有关如何在C#中传递参数的详细信息,请参阅此MSDN文章 。

为了达到你想要的效果,你应该在SomeOperationFunction创建一个列表的副本,然后返回它。 一个简单的例子:

 void List SomeOperationFunction(List target) { var newList = new List(target); newList.RemoveAt(3); return newList; // return copy of list } 

正如Olivier Jacot-Descombes在对另一个答案的评论中指出的那样,重要的是要记住这一点

[…]如果项目是引用类型,列表仍然保留对相同项目的引用。 因此,对项目本身的更改仍会影响两个列表中的项目。