2

I am trying to copy the list of items from list1 to another list list2. I'm able to do that. However I don't want the changes made in list2 tobe reflected in list1.

class Program
{
    static void Main(string[] args)
    {
        List<MyClass> list1 = new List<MyClass>();
        list1.Add(new MyClass(){ID = 1, Name = "name1"});
        list1.Add(new MyClass(){ID = 2, Name = "name2"});
        list1.Add(new MyClass(){ID = 3, Name = "name3"});

        //Copy items from list1 to list2
        List<MyClass> list2 = new List<MyClass>(list1);

        list1.ForEach(x => Console.WriteLine(x.Name));   //It shows the name as added

        //Empty items in list2
        list2.ForEach(x => x.Name = string.Empty);

        //Print items in list1
        list1.ForEach(x => Console.WriteLine(x.Name));   //It shows the name is empty
        Console.ReadKey();
    }
}

class MyClass
{
    private int id;

    public int ID
    {
        get { return id; }
        set { id = value; }
    }

    private string name;

    public string Name
    {
        get { return name; }
        set { name = value; }
    }
}

I hope there should be an easy way.

leppie
  • 115,091
  • 17
  • 196
  • 297
Gopichandar
  • 2,742
  • 2
  • 24
  • 54

3 Answers3

4

Since this is a reference type you are changing all. You need to create a copy of that instance:

public class MyClass 
{
    private int id;

    public int ID
    {
        get { return id; }
        set { id = value; }
    }

    private string name;

    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public MyClass Copy()
    { 
        return new MyClass
        {
            ID = this.ID,
            Name = this.Name
        };
    }
}

Now you can create the second list in this way:

List<MyClass> list2 = new List<MyClass>(list1.Select(x => x.Copy()));

Of course you don't need that method. You could do that also on-the-fly in the LINQ query:

List<MyClass> list2 = new List<MyClass>(list1.Select(x => new MyClass { ID = x.ID, Name = x.Name }));

Another similar approach is a copy-constructor. A copy-constructor is used to initialize an instance by providing another instance of the same type:

public class MyClass
{
    private int id;

    public MyClass(MyClass instance)
    {
        this.id = instance.ID;
        this.name = instance.Name;
    }

    public int ID
    {
        get { return id; }
        set { id = value; }
    }

    private string name;

    public string Name
    {
        get { return name; }
        set { name = value; }
    }
}

Now you could fill the list with copies in this way:

List<MyClass> list2 = new List<MyClass>(list1.Select(x => new MyClass(x)));

or with List.ConvertAll:

List<MyClass> list2 = list1.ConvertAll(x => new MyClass(x));
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
1

If you want to use this feature in other places too, I guess the best way to do is, to create an extention method for IList like this:

static class Extensions
{
   public static IList<T> Clone<T>(this IList<T> sourceList) 
      where T: ICloneable
   {
       return sourceList.Select(item => (T)item.Clone()).ToList();
   }
}

And to use it, you should change your class and make it ICloneable:

class MyList : ICloneable
{
    public MyList(int idParam, string nameParam)
    {
        ID = idParam;
        Name = nameParam;
    }
    public object Clone()
    {
        return new MyList(ID, Name);
    }

    private int id;
    public int ID
    {
        get { return id; }
        set { id = value; }
    }

    private string name;
    public string Name
    {
        get { return name; }
        set { name = value; }
    }
}
Artem Kulikov
  • 2,250
  • 19
  • 32
  • [Why no ICloneable?](http://stackoverflow.com/questions/536349/why-no-icloneablet) ... [Should we Obsolete ICloneable](http://blogs.msdn.com/b/brada/archive/2004/05/03/125427.aspx) – Tim Schmelter Oct 16 '15 at 09:55
1

If your elements are reference types and type implement ICloneable interface you clould do something like this:

list1.ForEach((item) =>
{
    list2.Add((ICloneable)item.Clone());
});

If your element type doesn't implement ICloneable, you clould create "copy" constructor and do something like this:

list1.ForEach((item)=>
    {
        list2.Add(new MyList(item.ID, item.Name));
    });
Mariusz
  • 442
  • 5
  • 16
  • 1
    The `IClonable` contract does not specify the type of the clone performed, different classes have different implementations. A consumer cannot rely on `ICloneable` to let them know whether an object is deep-cloned or not. For that reason it should be avoided. – Tim Schmelter Oct 16 '15 at 10:06