【问题标题】:How to keep a Generic list unmodified when its copy is modified?如何在修改其副本时保持通用列表不被修改?
【发布时间】:2012-11-06 04:00:42
【问题描述】:

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

【问题讨论】:

  • 向我们展示Logic.ModifyList 的作用。
  • 可能重复 - stackoverflow.com/questions/222598/… 您需要深度复制您的对象。
  • 您想“深度”复制列表。否则,两个列表中的引用都指向相同的学生对象。见stackoverflow.com/questions/4226747/deep-copy-of-listt
  • public static void ModifyList(List lstIntegers) { foreach (student s in lstIntegers) { if (s.ID.Equals("1")) { s.ID = "4"; s.Name = "APS"; } } }
  • @user1801934 Student 是您的班级(可以访问源代码)还是您无法控制?

标签: c# list


【解决方案1】:

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

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 的情况下的一个选项。适用时,对接口进行编码。也就是说,可以在student对象上实现ICloneable,在Clone()方法中使用BinaryFormatter逻辑;但是,作为开发人员,您可以选择自己决定要做什么。选项不一定是建议,建议并不总是一种选择。有时您必须尽一切可能完成任务,这就是选项发挥作用的地方。

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

【讨论】:

  • 如果 OP 需要一个列表的克隆,ICloneable 正是应该实现的接口。当行为与ICloneable 完全相同时,避免此接口有什么意义?
  • 我的解决方案提供了相同的结果。 OP 需要一个克隆,提供了 ICloneable,现在提供了 binaryformatter 选项。这是否违反要求?人生在于选择。当我没有源代码时,我不得不做出这样的决定。
  • 好吧,我想它可以在Clone() 实现中使用。不过,避免ICloneable 并不是一个好建议。该接口表示某种行为,并仅通过其存在来记录代码。我将 -1 你的答案,但如果你用ICloneable 更新你的答案并修复格式以便正确阅读,我会删除它。
  • 请编辑您的帖子以表明正在克隆的类,以及由其属性引用的所有其他类都需要为[Serializable]
  • Ehh.. 我会说我克隆整个对象图的解决方案已被广泛接受。 stackoverflow.com/questions/129389/…
【解决方案2】:

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

How do I clone a generic list in C#?

【讨论】:

    【解决方案3】:

    使用 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 来深度复制您的对象

    【讨论】:

      【解决方案4】:

      如果您的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 对象集 - 修改一个 Studentinstance 不会对您另一个列表中的学生产生影响。

      【讨论】:

      • public static void ModifyList(List lstIntegers) { foreach (student s in lstIntegers) { if (s.ID.Equals("1")) { s.ID = "4"; s.Name = "APS"; } } }
      • @user1801934 很抱歉没有尽快回复 - 我更新了我的答案。
      • @xxbbcc 你需要从函数中返回你的列表。
      • 公开课学生:ICloneable { public string ID { get;放; } 公共字符串名称 { 获取;放; } 公共对象 Clone() { 学生 newPerson = (Student)this.MemberwiseClone();返回新人; } }
      【解决方案5】:

      您想要列表的深层副本。现在,它是一个浅拷贝。你只有两个对同一个变量的引用。没有像 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);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-01-08
        • 2019-03-27
        • 2011-09-30
        • 1970-01-01
        • 2018-01-02
        相关资源
        最近更新 更多