3

当我在 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"; 
        } 
    } 
}
4

5 回答 5

3

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

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();但是,作为开发人员,您可以选择自己决定要做什么。选项不一定是建议,建议也不一定是选项。有时您必须尽其所能完成一项任务,这就是选择发挥作用的地方。

这是一种相当广泛接受的深度克隆方法: 如何在 .NET(特别是 C#)中对对象进行深度复制?

于 2012-11-06T04:18:38.210 回答
2

我会看看ICloneable。你所追求的是一个“深拷贝”。这篇文章中有很多很好的信息:

如何在 C# 中克隆通用列表?

于 2012-11-06T04:10:47.077 回答
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 来深度复制你的对象

于 2012-11-06T04:39:07.047 回答
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;

您还需要通过实现接口使您的Student类可克隆。ICloneable

不过不要忘记,在此之后,您将拥有 2 个独立的列表,其中包含 2 个独立的Student对象集 - 修改一个Student实例不会影响另一个列表中的学生。

于 2012-11-06T04:05:30.430 回答
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);
于 2012-11-06T04:45:43.517 回答