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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13977841/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 10:14:31  来源:igfitidea点击:

How do I change my new list without changing the original list?

c#

提问by user505210

I have a list that gets filled in with some data from an operation and I am storing it in the memory cache. Now I want another list which contains some sub data from the list based on some condition.

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

As can be seen in the below code I am doing some operation on the target list. The problem is that whatever changes I am doing to the target list is also being done to the mainList. I think its because of the reference is same or something.

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

All I need is that operation on the target list not affect data inside the main list.

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

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

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

采纳答案by Leri

You need to clone your list in your method, because List<T>is a class, so it's reference-type and is passed by reference.

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

For example:

例如:

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

Or

或者

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

or

或者

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

回答by kemiller2002

Your target variable is a reference type. This means that anything you do to it will be reflected in the list you pass into it.

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

To not do that, you are going to need to create a new list in the method, copy targetcontents to it, and then perform the remove at operation on the new list.

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

About Reference and Value Types

关于引用和值类型

回答by Residuum

Build a new list first and operate on that, because List is a reference type, i.e. when you pass it in a function, you do not just pass the value but the actual object itself.

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

If you just assign targetto mainList, both variables point to the same object, so you need to create a new List:

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

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

void List<Item> SomeOperationFunction()makes no sense, because either you return nothing (void) or you return a List<T>. So either remove the return statement from your method or return a new List<Item>. In the latter case, I would rewrite this as:

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;
}

回答by Servy

You need to make a copy of the list so that changes to the copy won't affect the original. The easiest way to do that is to use the ToListextension method in System.Linq.

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

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

回答by Jesse Webb

Your are seeing the original list being modified because, by default, any non-primitive objects, are passed by reference (It is actually pass by value, the value being the reference, but that is a different matter).

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

What you need to do is clone the object. This question will help you with some code to clone a List in C#: How do I clone a generic list in C#?

您需要做的是克隆对象。这个问题将帮助您使用一些代码来克隆 C# 中的列表:How do I clone a generic list in C#?

回答by bernhof

Since a Listis a reference type, what is passed to the function is a reference to the original list.

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

See this MSDN articlefor more information about how parameters are passed in C#.

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

In order to achieve what you want, you should create a copy of the list in SomeOperationFunctionand return this instead. A simple example:

为了实现您想要的,您应该在其中创建列表的副本SomeOperationFunction并返回它。一个简单的例子:

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

As pointed out by Olivier Jacot-Descombes in the comments to another answer, it is important to bear in mind that

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

[...] the list still holds references to the same items if the items are of a reference type. So changes to the items themselves will still affect the items in both lists.

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

回答by Sam Axe

Instead of assigning mainList to target, I would do: target.AddRange(mainList);

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

Then you will have a copy of the items instead of a reference to the list.

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

回答by Kavet Kerek

Just make sure that you initialize the new list with a list created by copying the elements of the source list.

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

List<Item> target = mainList;Should be List<item> target = new List<Item>(mainList);

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

回答by Nadir Sampaoli

You'll need to make a copy of the list since in your original code what you're doing is just passing around, as you correctly suspected, a reference (someone would call it a pointer).

您需要制作列表的副本,因为在您的原始代码中,您所做的只是传递,正如您正确怀疑的那样,一个引用(有人会称其为指针)。

You could either call the constructor on the new list, passing the original list as parameter:

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

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

Or create a MemberWiseClone:

或者创建一个MemberWiseClone

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

Also, you are not storing the return of SomeOperationFunctionanywhere, so you might want to revise that part as well (you declared the method as void, which should not return anything, but inside it you're returning an object). You should call the method this way:

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

List<Item> target = SomeOperationFunction(mainList);

Note:the elements of the list will not be copied (only their reference is copied), so modifying the internal state of the elements will affect both lists.

注:列表中的内容不会被复制(只有他们引用复制),所以修改元素的内部状态,会影响这两个列表。

回答by Quanta

Even if you create a new list, the references to the items in the new list will still point to the items in the old list, so I like to use this extension method if I need a new list with new references...

即使你创建了一个新列表,对新列表中项目的引用仍然会指向旧列表中的项目,所以如果我需要一个带有新引用的新列表,我喜欢使用这种扩展方法......

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();
}