0

I have this list of custom class objects:

List<URL> URLs = new List<URL>();

and I'm trying to push a copy on to a Stack:

Stack<List<URL>> undo = new Stack<List<URL>>();

List<URL> temp = new List<URL>();
temp.AddRange(new List<URL>(URLs));
undo.Push(temp);

Now whenever I delete an object from the original (URLs) list everything is OK with the one on the stack (temp). But when I delete a list element from a list inside the object in the original (URLs) the same element dissapears in the copy of that object's list that is on the stack.

I delete objects from URLs the same way I delete list elements inside that object. Does anyone know why this is happening?

Igor
  • 1,532
  • 4
  • 23
  • 44

5 Answers5

1

That's because List is basically an array i.e. a reference type. They will only have reference of each other.

make a Deep Copy using extension method

public static class ExtensionMethod
{  
    public static T DeepClone<T>(this T element)
    { 
        MemoryStream m = new MemoryStream();
        BinaryFormatter b = new BinaryFormatter();
        b.Serialize(m, element);
        m.Position = 0;
        return (T)b.Deserialize(m);
    }
}

and make sure that u declare your URL Class like this

[Serializable]
public class URL
{
   //..... Your Class
}

and call it like

temp.AddRange(URLs.DeepClone().ToList());
Nikhil Agrawal
  • 47,018
  • 22
  • 121
  • 208
  • well, any ideas how to fix this? – Igor Apr 28 '12 at 06:59
  • and where do I put the first part? Anywhere or in the class? – Igor Apr 28 '12 at 07:15
  • make a new static class. These are Extension Methods(http://msdn.microsoft.com/en-us/library/bb383977.aspx) and make sure you write this as it is otherwise this (URLs.DeepCopy()) won't work – Nikhil Agrawal Apr 28 '12 at 07:24
  • DeepCopy() is not accesible through URLs – Igor Apr 28 '12 at 07:40
  • I meant URLs.DeepCopy() doesn't work. I can't find DeepCopy() when I type the dot. – Igor Apr 28 '12 at 07:48
  • Have you made that function DeepCopy. Its not a predefined function. You have to create one. If you are on teamviewer please provide your teamviewer id and teamviewer password so that i can remote view your problem. – Nikhil Agrawal Apr 28 '12 at 08:04
  • No I haven't, thought it's predefined. – Igor Apr 28 '12 at 08:06
  • @Nikhill Agrawal Doesn't this: `temp.AddRange(new List(URLs));` do a deep copy because of the 'new'? – Igor Apr 28 '12 at 08:19
  • yes `new` does that but the contents of list are the same as that of URLs. No Matter how many new copy of URLs you make it will all be same because actual value are not stored in it but the address in memory where they are located. – Nikhil Agrawal Apr 28 '12 at 08:31
  • so it doesn't make any difference. Can make the DeepCopy function? – Igor Apr 28 '12 at 08:39
  • @Nikhill Agrawal and is [this](http://stackoverflow.com/questions/2314496/c-sharp-list-member-needs-deep-copy) answer wrong? Seem to do the same thing I'm trying to. – Igor Apr 28 '12 at 08:45
  • No that answer is right. There List is of int which are value type. Your list is of URL Class. Class is reference type. – Nikhil Agrawal Apr 28 '12 at 08:51
  • No, not yet, I'm trying to figure out how to do the DeepCopy() function. – Igor Apr 28 '12 at 09:00
  • Make sure class Extensions is also public. I have edited my answer to make Extensions class public. – Nikhil Agrawal Apr 28 '12 at 09:04
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/10634/discussion-between-nikhil-agrawal-and-igor) – Nikhil Agrawal Apr 28 '12 at 09:09
0

You need create clone

static class Extensions
{
        public static IList<T> Clone<T>(this IList<T> listToClone) where T: ICloneable
        {
                return listToClone.Select(item => (T)item.Clone()).ToList();
        }
}
Mediator
  • 14,951
  • 35
  • 113
  • 191
0

A list or stack only holds references to the object it contains. Both your lists hold references to the same objects. So if you change an object reached from one list, that's the same object that's reached from the other list.

As @simply denis suggests you can clone the objects when you create the second list, so that each list has it's own copy of the objects.

Another solution is to use immutable objects. For example the string class in .NET is immutable. You cannot change a string once you've created it. All string manipulation routines return a new string. Immutable types effectively prevents this kind of problems as any manipulation automatically creates a new instance.

Anders Abel
  • 67,989
  • 17
  • 150
  • 217
  • immutable is a dangerous word here as there are many kinds of immutability in C# (for example, a readonly stack of reference objects may be immutable, but it's items can still be changed.) More appropriately, the original poster needs to use value types as the items in their collection. – Sprague Apr 28 '12 at 07:11
  • A .NET value type (built in or a `struct`) is always immutable, but there are also immutable string types. E.g. the [MSDN doc](http://msdn.microsoft.com/en-us/library/362314fe.aspx) for `string` uses the term immutable *Strings are immutable--the contents of a string object cannot be changed* – Anders Abel Apr 28 '12 at 07:15
  • I'm simply pointing out that there are problems with immutability in C#, using an immutable type will not always solve the original posters problem, and may lead them down a deeper path. More importantly, the OP should understand how object references work and why this is the real cause of his problem. – Sprague Apr 28 '12 at 07:20
  • string may be a reference type but it has a significant amount of special treatment in the framework to make it behave as it it weren't – Sprague Apr 28 '12 at 07:24
0

That's because you are making a shallow copy of the list. You get two lists with references to the same URL objects.

(Note that you are actually making two shallow copies, the new List<URL>(URLs) makes an intermediate copy, which you only read from and then throw away.)

If you want a deep copy, you have to create new URL instances for the copy. I don't know what you have implemented in the URL class, but basically:

List<URL> temp = new List<URL>(URLs.Select(u => u.Clone()));
Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • Can you be more specific now that i've included the class – Igor Apr 28 '12 at 07:23
  • @Igor: Ow, that'l not be efficient... You need a method that creates a new instance of `URL` by copying the contenst of an existing, and you have to make deep copies of the lists in the object. You can use serialising as Nikhil suggestsed, but that's not very efficient, and you need a parameterless constructor for that to work. – Guffa Apr 28 '12 at 07:56
0

This is because the URL objects in undo are referenced to the same object as those URL objects in URLs. Thus modifying the one affects another. What you need to do is making a deep copy of the URL objects. How to make a deep copy really depends on the implementation of your URL class. Here is a general way: How do you do a deep copy of an object in .NET (C# specifically)?

But note that you may probably have a better/more efficient way to clone your URL object. You need to show us the implementation of the class first.

Community
  • 1
  • 1
Cheng Chen
  • 42,509
  • 16
  • 113
  • 174
  • Doesn't the 'new' constructor make a new list instead of a refference here: temp.AddRange(new List(URLs)); – Igor Apr 28 '12 at 08:09