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

时间:2012-12-20 17:58:19

标签: c#

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

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

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

List<Item> target = mainList;
SomeOperationFunction(target);

 void List<Item> SomeOperationFunction(List<Item> target)
{
  target.removeat(3);
  return target;
}

11 个答案:

答案 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内容复制到其中,然后在新列表上执行删除操作。

About Reference and Value Types

答案 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);