3

When I create a copy of the original list lstStudent in lstCopy and send the lstCopy to modification function, the lstStudent also gets modified. I want to keep this list unmodified.

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"; 
        } 
    } 
}
Brian Rasmussen
  • 114,645
  • 34
  • 221
  • 317
Akash Mehta
  • 2,779
  • 2
  • 15
  • 10

5 Answers5

3

You can also achieve this without cloneable interface by using binary formatter: NOTE: ALL classes as part of the object graph must be marked 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; }
}

OUTPUT:

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

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

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

code is formatted for dumping into LINQPad

EDIT: This is an option in situations where it's not possible to implement ICloneable. When applicable, code to interfaces. In other words, you can implement ICloneable on the student object and use the BinaryFormatter logic in the Clone() method; However, As a developer, you have the option to decide for yourself what you want to do. Options are not necessarily advice and advice isn't always an option. There are times when you must do what it takes to complete a task and that's where options come into play.

This is a pretty widely accepted, deep cloning, method: How do you do a deep copy of an object in .NET (C# specifically)?

Community
  • 1
  • 1
Hardrada
  • 728
  • 8
  • 19
  • If the OP needs a clone of the list, `ICloneable` is precisely the interface that should be implemented. What's the point of avoiding this interface when the behavior is exactly that of `ICloneable`? – xxbbcc Nov 06 '12 at 04:22
  • 1
    My solution provides the same result. The OP needs a clone, ICloneable was offered and now binaryformatter option. Does that violate the request? Life is about options. I've had to make decisions like this when I didn't have source code. – Hardrada Nov 06 '12 at 04:26
  • Well, I guess it could be used inside a `Clone()` implementation. Avoiding `ICloneable` is not good advice though. This interface signifies a certain behavior and documents the code just by its presence. I'm going to -1 your answer but I'll remove it if you update your answer with `ICloneable` and fix the formatting for proper reading. – xxbbcc Nov 06 '12 at 04:29
  • 2
    Please edit your post to indicate that the class being cloned, and all others referenced by its properties need to be `[Serializable]`. – John Saunders Nov 06 '12 at 04:37
  • Ehh.. I'd say my solution to cloning an entire object graph is pretty widely accepted. http://stackoverflow.com/questions/129389/how-do-you-do-a-deep-copy-an-object-in-net-c-specifically – Hardrada Nov 06 '12 at 04:42
  • @Hardrada I removed my -1 after your update that includes `ICloneable`. You should still reformat the code for humans. – xxbbcc Nov 06 '12 at 05:03
  • @xxbbcc Thank you kindly! I did apply indentation for better readability. What other formatting are you referring to? – Hardrada Nov 06 '12 at 05:04
  • @Hardrada I meant mostly indentation inside `main()` and `Clone()` functions - the code starts on the same level where `public` is which makes it hard to read. You also have some curly braces on the end of the line and some on the following line - typically this is cleaned up by either the poster or someone else here on SO. It's not much of my business, though, as long as the community is fine with it, you're good. :) I also ended up with upvoting your answer since the idea you presented (with the details added) is useful. – xxbbcc Nov 06 '12 at 13:37
  • @xxbbcc addressed. Thanks for the upvote. Open minds always win. – Hardrada Nov 06 '12 at 13:41
2

I would take a look at ICloneable. What you're after is a "deep copy". Lots of good information in this post:

How do I clone a generic list in C#?

Community
  • 1
  • 1
AnthonyRyan
  • 153
  • 1
  • 7
1

Just another quick way to copy your List using 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);

But the best option would still be implementing ICloneable to deep copy your objects

Prabhu Murthy
  • 9,031
  • 5
  • 29
  • 36
0

If your Student type is a class (reference type), a list will only contain references to those instances. This means that when you make a copy of your list, the copied list will also have only references that still point to the same Student instances. By copying your list you simply duplicated your references but not the instances. What you have after the copy is something like this:

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

So if you do something like list1[0].Name = "Jim", you'll update the instance S1 and you'll see the change in both lists, since both lists refer to the same set of instances.

What you need to do is create a clone of not only the list in this case but all the objects inside the list - you need to clone all your Student objects as well. This is called a deep copy. Something like this:

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

You call this like so:

StudentList oClonedList = lstStudent.Clone () as StudentList;

You also need to make your Student class cloneable by implementing the ICloneable interface.

Don't forget, though, that after this, you'll have 2 separate lists with 2 independent sets of Student objects - modifying one Studentinstance will have no effect on students in your other list.

xxbbcc
  • 16,930
  • 5
  • 50
  • 83
  • public static void ModifyList(List lstIntegers) { foreach (Student s in lstIntegers) { if (s.ID.Equals("1")) { s.ID = "4"; s.Name = "APS"; } } } – Akash Mehta Nov 06 '12 at 04:07
  • @user1801934 Sorry for not replying sooner - I updated my answer. – xxbbcc Nov 06 '12 at 04:12
  • @xxbbcc You need to return your list from the funtion. –  Nov 06 '12 at 04:14
  • public class Student : ICloneable { public string ID { get; set; } public string Name { get; set; } public object Clone() { Student newPerson = (Student)this.MemberwiseClone(); return newPerson; } } – Akash Mehta Nov 06 '12 at 07:00
0

You want a deep copy of the list. Right now, it is a shallow copy. You just have two references to the same variable. There is no simple way to get a deep copy in C# like in languages such as C++. One method to get a deep copy is by serializing and deserializing the first list. The following is a templated function to get a deep copy of an object.

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

You will need to include these namespaces as well:

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

You can then call the function like this:

lstCopy = getDeepCopy<List<Student>>(lstStudents);
noddy
  • 3,827
  • 3
  • 24
  • 21