如何在修改副本时保持通用列表不被修改?

时间:2012-11-06 04:00:42

标签: c# list

当我在lstCopy中创建原始列表lstStudent的副本并将lstCopy发送到修改函数时,lstStudent也会被修改。我想保持这个列表不被修改。

List<Student> lstStudent = new List<Student>();
Student s = new Student();
s.Name = "Akash";
s.ID = "1";
lstStudent.Add(s);
List<Student> lstCopy = new List<Student>(lstStudent);
Logic.ModifyList(lstCopy);
// "Want to use lstStudent(original list) for rest part of the code"

public static void ModifyList(List<Student> lstIntegers) { 
    foreach (Student s in lstIntegers) { 
        if (s.ID.Equals("1")) { 
            s.ID = "4"; s.Name = "APS"; 
        } 
    } 
}

5 个答案:

答案 0 :(得分:3)

使用二进制格式化程序,您也可以在没有可克隆接口的情况下实现此目的: 注意:作为对象图的一部分的所有类必须标记为Serializable。

void Main()
{
   var student1=new Student{Name="Bob"};
   var student2=new Student{Name="Jim"};

   var lstStudent=new List<Student>();
   lstStudent.Add(student1);
   lstStudent.Add(student2);

   var lstCopy=new List<Student>(lstStudent);
   var clone=Clone(lstStudent);

   student1.Name="JOE";

   lstCopy.Dump();
   lstStudent.Dump();
   clone.Dump();

}

public List<Student> Clone(List<Student> source)
{

   BinaryFormatter bf=new BinaryFormatter();
   using(var ms=new MemoryStream())
   {
     bf.Serialize(ms,source);
     ms.Seek(0,0);
     return (List<Student>)bf.Deserialize(ms);
   }
}

[Serializable]
public class Student
{
  public string Name { get; set; }
}

输出:

5List<Student> (2 items) 4  
Name 
JOE 
Jim 

5List<Student> (2 items) 4  
Name 
JOE 
Jim 

5List<Student> (2 items) 4  
Name 
Bob 
Jim 

代码格式化为转储到LINQPad

编辑: 在无法实现ICloneable的情况下,这是一个选项。适用时,代码接口。换句话说,您可以在学生对象上实现ICloneable并使用BinaryFormatter方法中的Clone()逻辑;但是,作为开发人员,您可以选择自己决定要执行的操作。选项不一定是建议,建议并不总是一种选择。有些时候你必须完成任务所需的工作,这就是选项发挥作用的地方。

这是一种广泛接受的深度克隆方法: How do you do a deep copy of an object in .NET (C# specifically)?

答案 1 :(得分:2)

我会看看ICloneable。你所追求的是一个&#34;深拷贝&#34;。这篇文章中有很多好消息:

How do I clone a generic list in C#?

答案 2 :(得分:1)

使用LINQ

复制列表的另一种快捷方法
List<student> oldList = new List<student> { new student{
                                             id=122,
                                             name="John"} };

IEnumerable<student> copy= oldList.Select(item => new student{
                                             id = item.id,
                                             name = item.name });

List<student> newList= new List<student>(copy);

但最好的选择仍然是实施ICloneable来深度复制你的对象

答案 3 :(得分:0)

如果您的Student类型是类(引用类型),则列表将仅包含对这些实例的引用。这意味着当您复制列表时,复制的列表也将只包含仍指向相同Student实例的引用。通过复制列表,您只需复制引用但不复制实例。副本之后的内容是这样的:

List 1            List 2                     instance S1
ref to S1         ref to S1                  Name: Akash
ref to S2         ref to S2                  ...
...               ...

因此,如果您执行list1[0].Name = "Jim"之类的操作,您将更新实例S1,并且您将看到两个列表中的更改,因为两个列表都引用了同一组实例。

您需要做的是创建一个不仅包含此情况下的列表,而且还包含里面的所有对象的克隆 - 您还需要克隆所有Student个对象。这称为深拷贝。像这样:

class StudentList : List<Student>, ICloneable
{
    public object Clone ()
    {
        StudentList oNewList = new StudentList ();

        for ( int i = 0; i < Count; i++ )
        {
            oNewList.Add ( this[i].Clone () as Student );
        }

        return ( oNewList );
    }
}

你这样称呼:

StudentList oClonedList = lstStudent.Clone () as StudentList;

您还需要通过实施ICloneable接口来克隆Student类。

不要忘记,在此之后,您将拥有2个单独的列表和2个独立的Student个对象 - 修改一个Student实例对您其他人的学生没有任何影响列表。

答案 4 :(得分:0)

您需要列表的深层副本。现在,这是一个浅薄的副本。您只有两个对同一变量的引用。没有简单的方法可以像C ++这样的语言在C#中获得深层复制。获取深层副本的一种方法是序列化和反序列化第一个列表。以下是获取对象深层副本的模板化函数。

public static T getDeepCopy<T>( T objectToCopy )
{
    T temp;
    using ( MemoryStream ms = new MemoryStream() )
    {
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Serialize( ms, objectToCopy );
        ms.Position = 0;
        temp = (T)formatter.Deserialize( ms );
    }
    return temp;
}

您还需要包含这些命名空间:

using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

然后您可以像这样调用函数:

lstCopy = getDeepCopy<List<Student>>(lstStudents);