14

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

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

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

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

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

11 回答 11

24

您需要在您的方法中克隆您的列表,因为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;
}
于 2012-12-20T18:02:14.537 回答
7

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

var newList = SomeOperationFunction(target.ToList());
于 2012-12-20T18:02:36.150 回答
5

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

如果只是赋值targetmainList,两个变量都指向同一个对象,所以需要新建一个List:

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

void List<Item> SomeOperationFunction()没有意义,因为要么你什么都不返回 ( void) 要么你返回 a 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;
}
于 2012-12-20T18:02:18.980 回答
3

我尝试了上面的许多答案。在我测试的所有列表中,对新列表的更新都会修改原始列表。这对我有用。

var newList = JsonConvert.DeserializeObject<List<object>>(JsonConvert.SerializeObject(originalList));
return newlist.RemoveAt(3);
于 2019-10-29T02:44:54.837 回答
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();
}
于 2012-12-20T18:55:37.043 回答
1

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

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

为了实现您想要的,您应该在其中创建列表的副本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 在对另一个答案的评论中指出的那样,重要的是要记住

[...] 如果项目属于引用类型,则列表仍然包含对相同项目的引用。因此,对项目本身的更改仍会影响两个列表中的项目。

于 2012-12-20T18:03:35.610 回答
1

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

为了不这样做,您需要在方法中创建一个新列表,将target内容复制到其中,然后对新列表执行 remove at 操作。

关于引用和值类型

于 2012-12-20T18:01:14.230 回答
0

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

List<Item> target = mainList;应该List<item> target = new List<Item>(mainList);

于 2012-12-20T18:10:00.190 回答
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);

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

于 2012-12-20T18:13:22.767 回答
0

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

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

于 2012-12-20T18:03:31.857 回答
0

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

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

于 2012-12-20T18:04:13.377 回答