我有一个列表,其中包含来自操作的一些数据,我将其存储在内存缓存中。现在我想要另一个列表,其中包含基于某些条件的列表中的一些子数据。
从下面的代码可以看出,我正在对目标列表进行一些操作。问题是我对目标列表所做的任何更改也都在对mainList进行。我认为它因为引用是相同或类似的。
我需要的是目标列表上的操作不会影响主列表中的数据。
List<Item> target = mainList;
SomeOperationFunction(target);
void List<Item> SomeOperationFunction(List<Item> target)
{
target.removeat(3);
return target;
}
答案 0 :(得分:19)
您需要在方法中克隆列表,因为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;
}
答案 1 :(得分:6)
您需要复制一份列表,以便对副本的更改不会影响原件。最简单的方法是使用ToList
中的System.Linq
扩展方法。
var newList = SomeOperationFunction(target.ToList());
答案 2 :(得分:4)
首先构建一个新列表并对其进行操作,因为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;
}
答案 3 :(得分:2)
即使你创建了一个新列表,对新列表中项目的引用仍然会指向旧列表中的项目,所以如果我需要一个带有新引用的新列表,我喜欢使用这个扩展方法。
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();
}
答案 4 :(得分:1)
您的目标变量是引用类型。这意味着您对它所做的任何事情都会反映在您传递给它的列表中。
要不这样做,您需要在方法中创建一个新列表,将target
内容复制到其中,然后在新列表上执行删除操作。
答案 5 :(得分:0)
您正在查看正在修改的原始列表,因为默认情况下,任何非基本对象都是通过引用传递的(实际上是按值传递,值是引用,但这是另一回事)。
您需要做的是克隆对象。这个问题将帮助您使用一些代码来克隆C#中的List:How do I clone a generic list in C#?
答案 6 :(得分:0)
由于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在对另一个答案的评论中指出,重要的是要记住
[...]如果列表仍然保留对相同项目的引用 这些项目是参考类型。所以改变了物品本身 仍然会影响两个列表中的项目。
答案 7 :(得分:0)
我没有将mainList分配给目标,而是执行:target.AddRange(mainList);
然后您将获得项目的副本,而不是对列表的引用。
答案 8 :(得分:0)
只需确保使用通过复制源列表元素创建的列表初始化新列表。
List<Item> target = mainList;
应为List<item> target = new List<Item>(mainList);
答案 9 :(得分:0)
你需要制作一份列表副本,因为在你的原始代码中你正在做的只是传递,正如你所怀疑的那样,一个引用(某人称之为指针)。
您可以在新列表上调用构造函数,将原始列表作为参数传递:
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);
注意:列表的元素不会被复制(只有它们的引用被复制),因此修改元素的内部状态将影响两个列表。< / p>
答案 10 :(得分:0)
我尝试了以上许多答案。在我测试的所有产品上,对新列表的更新都会修改原始列表。这对我有用。
var newList = JsonConvert.DeserializeObject<List<object>>(JsonConvert.SerializeObject(originalList));
return newlist.RemoveAt(3);