如何在不更改原始列表的情况下更改新列表?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在不更改原始列表的情况下更改新列表?相关的知识,希望对你有一定的参考价值。
我有一个列表,其中填充了操作中的一些数据,并将其存储在内存缓存中。现在,我想要另一个包含基于某些条件的列表中的子数据的列表。
如下面的代码所示,我正在目标列表上进行一些操作。问题是我对目标列表所做的任何更改也正在对mainList进行。我认为这是因为引用是相同的。
我需要的是对目标列表的操作不会影响主列表中的数据。
List<Item> target = mainList;
SomeOperationFunction(target);
void List<Item> SomeOperationFunction(List<Item> target)
{
target.removeat(3);
return target;
}
您需要在您的方法中克隆列表,因为List<T>
是一个类,所以它是引用类型,并通过引用传递。
例如:
List<Item> SomeOperationFunction(List<Item> target)
{
List<Item> tmp = target.ToList();
tmp.RemoveAt(3);
return tmp;
}
或
List<Item> SomeOperationFunction(List<Item> target)
{
List<Item> tmp = new List<Item>(target);
tmp.RemoveAt(3);
return tmp;
}
或
List<Item> SomeOperationFunction(List<Item> target)
{
List<Item> tmp = new List<Item>();
tmp.AddRange(target);
tmp.RemoveAt(3);
return tmp;
}
List
是引用类型,所以传递给函数的是对原始列表的引用。有关如何在C#中传递参数的更多信息,请参见MSDN article。>
为了实现所需的功能,您应该在SomeOperationFunction
中创建列表的副本,然后将其返回。一个简单的例子:
void List<Item> SomeOperationFunction(List<Item> target)
{
var newList = new List<Item>(target);
newList.RemoveAt(3);
return newList; // return copy of list
}
[正如Olivier Jacot-Descombes在对另一个答案的评论中所指出的,牢记这一点很重要
[...]列表仍然保留对相同项目的引用,如果这些项目属于参考类型。所以改变物品本身仍会影响两个列表中的项目。
您需要复制列表,以便对副本所做的更改不会影响原始列表。最简单的方法是在ToList
中使用System.Linq
扩展方法。
var newList = SomeOperationFunction(target.ToList());
[首先构建一个新列表并对其进行操作,因为List是一种引用类型,即,当您在函数中传递它时,您不仅传递值,而且传递实际对象本身。
如果仅将target
分配给mainList
,则两个变量都指向同一个对象,因此您需要创建一个新的列表:
List<Item> target = new List<Item>(mainList);
void List<Item> SomeOperationFunction()
没有意义,因为您什么也不返回(void
)或返回了List<T>
。因此,从您的方法中删除return语句或返回一个新的List<Item>
。在后一种情况下,我将其重写为:
List<Item> target = SomeOperationFunction(mainList);
List<Item> SomeOperationFunction(List<Item> target)
{
var newList = new List<Item>(target);
newList.RemoveAt(3);
return newList;
}
即使创建新列表,对新列表中项目的引用仍将指向旧列表中的项目,因此,如果我需要带有新引用的新列表,我喜欢使用此扩展方法...
public static IEnumerable<T> Clone<T>(this IEnumerable<T> target) where T : ICloneable
{
If (target.IsNull())
throw new ArgumentException();
List<T> retVal = new List<T>();
foreach (T currentItem in target)
retVal.Add((T)(currentItem.Clone()));
return retVal.AsEnumerable();
}
您的目标变量是引用类型。这意味着您对它所做的任何事情都会反映在您传递给它的列表中。
为此,您将需要在方法中创建一个新列表,将target
内容复制到其中,然后在对新列表执行操作时执行删除操作。
About Reference and Value Types
您正在查看原始列表,因为默认情况下,所有非原始对象都是通过引用传递的(实际上是通过值传递,该值是引用的传递,但这是另一回事)。
您需要做的就是克隆对象。此问题将通过一些代码帮助您克隆C#中的列表:How do I clone a generic list in C#?
而不是将mainList分配给目标,我会这样做:target.AddRange(mainList);
然后您将获得项目的副本,而不是列表的引用。
只需确保使用复制源列表元素创建的列表来初始化新列表。
[List<Item> target = mainList;
应为List<item> target = new List<Item>(mainList);
您需要复制列表,因为在您的原始代码中,您正在做的事情只是绕过了(如您正确怀疑的那样)一个引用(有人称其为指针)。
您可以在新列表上调用构造函数,并将原始列表作为参数传递:
List<Item> SomeOperationFunction(List<Item> target)
{
List<Item> result = new List<Item>(target);
result.removeat(3);
return result;
}
或创建MemberWiseClone:
List<Item> SomeOperationFunction(List<Item> target)
{
List<Item> result = target.MemberWiseClone();
result.removeat(3);
return result;
}
此外,您也不会在任何地方存储SomeOperationFunction
的返回值,因此您可能还希望修改该部分(您将方法声明为void
,该方法不应返回任何内容,但在其中返回的是宾语)。您应该以这种方式调用该方法:
List<Item> target = SomeOperationFunction(mainList);
注:列表中的元素将不会被复制(只有它们的引用已被[[copied),因此修改元素的内部状态将影响两个列表。
以上是关于如何在不更改原始列表的情况下更改新列表?的主要内容,如果未能解决你的问题,请参考以下文章
展开并在行中展开时,如何在不更改布局的情况下包装小部件列表?
如何在不更改现有组件文件的情况下通过插件将日期类型更改为下拉列表
如何在不刷新网页的情况下从下拉列表索引更改事件的数据库中检索数据