Deep copy ArrayList problem.

S

Steven Blair

I need to perform a Deep Copy on an ArrayList.

I wrote a small sample app to prove this could be done:

ArrayList a = new ArrayList();
ArrayList b = new ArrayList();

a.Add("Hello");
b = (ArrayList) a.Clone();

a[0] = "World";

This appears to work fine. However, when I try it in my application,
both ArrayLists point to the same memory.

Each element in my ArrayList is a custom class I have written, so I am
wondering would this have any impact on the call to Clone() ?

At a high level, what I need to do is create a very large structure once
on startup (the ArrayList will be static) and when each request comes in
from a client, I take a deep copy of the structure and allow each client
their own working copy. This would cut down a massive amoutn of
processing on my part.

Can anyone help?

Steven
 
D

daa

try:

ArrayList a = new ArrayList();
ArrayList b = new ArrayList();

a.Add("Hello");
b = a;
 
C

Chris Dunaway

Steven said:
I need to perform a Deep Copy on an ArrayList.

I wrote a small sample app to prove this could be done:

ArrayList a = new ArrayList();
ArrayList b = new ArrayList();

a.Add("Hello");
b = (ArrayList) a.Clone();

a[0] = "World";

This appears to work fine. However, when I try it in my application,
both ArrayLists point to the same memory.

I think you can use the ArrayList.CopyTo along with AddRange method:

ArrayList a = new ArrayList();
ArrayList b = new ArrayList();

a.Add("Hello");

string[a.Count] strings;
a.CopyTo(strings);

b.AddRange(strings);

Check the syntax, but I think this may work.
 
J

Jon Skeet [C# MVP]

Steven Blair said:
I need to perform a Deep Copy on an ArrayList.

I wrote a small sample app to prove this could be done:

ArrayList a = new ArrayList();
ArrayList b = new ArrayList();

a.Add("Hello");
b = (ArrayList) a.Clone();

a[0] = "World";

This appears to work fine. However, when I try it in my application,
both ArrayLists point to the same memory.

Could you post a short but complete program which demonstrates the
problem?

See http://www.pobox.com/~skeet/csharp/complete.html for details of
what I mean by that.
Each element in my ArrayList is a custom class I have written, so I am
wondering would this have any impact on the call to Clone() ?

Well, each list will refer to the same set of objects to start with,
although the lists themselves are independent. If you want the objects
themselves to be cloned (as your subject line suggests) you'll need to
call Clone() on each of the objects, eg:

for (int i=0; i < copiedList.Count; i++)
{
copiedList = copiedList.Clone();
}
 
J

Jon Skeet [C# MVP]

daa said:
try:

ArrayList a = new ArrayList();
ArrayList b = new ArrayList();

a.Add("Hello");
b = a;

That ends up with a single list, with two variables referring to the
same list. In fact, assigning a new ArrayList reference to b to start
with is pointless.
 
S

Steven Blair

Thanks for the replies.

Here is an example which closely matches the problem:

static void Main(string[] args)
{
ArrayList a = new ArrayList();
ArrayList b = new ArrayList();

a.Add( new ExampleClass("Hello") );
b = (ArrayList) a.Clone(); //I expected this to clone the whole
structure.

((ExampleClass)a[0]).m_Value = "World"; //This changes b[0] as well!
}

public class ExampleClass
{
public string m_Value;

public ExampleClass(string s)
{
m_Value = s;
}

public override string ToString()
{
return m_Value;
}
}
 
S

Steven Blair

Jon,

Since each class is custom, I can only call Clone on the ArrayList
itself:

a.Clone();

and not

a.Clone();
 
F

Frans Bouma [C# MVP]

Steven said:
I need to perform a Deep Copy on an ArrayList.

I wrote a small sample app to prove this could be done:

ArrayList a = new ArrayList();
ArrayList b = new ArrayList();

a.Add("Hello");
b = (ArrayList) a.Clone();

a[0] = "World";

This appears to work fine. However, when I try it in my application,
both ArrayLists point to the same memory.

Each element in my ArrayList is a custom class I have written, so I am
wondering would this have any impact on the call to Clone() ?

At a high level, what I need to do is create a very large structure
once on startup (the ArrayList will be static) and when each request
comes in from a client, I take a deep copy of the structure and allow
each client their own working copy. This would cut down a massive
amoutn of processing on my part.

Can anyone help?

MemoryStream stream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, a);
stream.Seek(0, SeekOrigin.Begin);
ArrayList b = (ArrayList)formatter.Deserialize(stream);
stream.Close();
stream.Dispose();

This gives you perfectly deep copies of a complete object graph.

(disclaimer: written from my bare head so it might have some small
syntax errors here and there)

FB

--
------------------------------------------------------------------------
Lead developer of LLBLGen Pro, the productive O/R mapper for .NET
LLBLGen Pro website: http://www.llblgen.com
My .NET blog: http://weblogs.asp.net/fbouma
Microsoft MVP (C#)
------------------------------------------------------------------------
 
S

Steven Blair

Frans,

Thanks for the reply.

Is this code a hit on performance?

I need to determine if creating a deep copy is more expensive than
actually building the structure from scratch each time.

My original structure has seem times of >50ms to construct!
 
J

Jon Skeet [C# MVP]

Steven said:
Thanks for the reply.

Is this code a hit on performance?

Yes, that will be a hit on performance.
I need to determine if creating a deep copy is more expensive than
actually building the structure from scratch each time.

My original structure has seem times of >50ms to construct!

A much better idea than serialization *if* you're in control of all the
objects is to implement your own Clone method, and invoke it on each of
the objects in the list.

Jon
 
S

Steven Blair

Ok, using the example above, here is what I think the finished code
looks like:

static void Main(string[] args)
{
ArrayList a = new ArrayList();
ArrayList b = new ArrayList();

ExampleClass t = new ExampleClass("Hello");

a.Add( t );

//At this point, I would loop round the count of the ArrayList
b.Add( ((ExampleClass)a[0]).Clone() );

((ExampleClass)a[0]).m_Value = "World";
}

public class ExampleClass : ICloneable
{
public string m_Value;

public ExampleClass(string s)
{
m_Value = s;
}

public override string ToString()
{
return m_Value;
}

object ICloneable.Clone()
{
return this.Clone();
}

public virtual ExampleClass Clone()
{
return this.MemberwiseClone() as ExampleClass;
}
}

Any final comments?
 
J

Jon Skeet [C# MVP]

Steven said:
Ok, using the example above, here is what I think the finished code
looks like:

static void Main(string[] args)
{
ArrayList a = new ArrayList();
ArrayList b = new ArrayList();

ExampleClass t = new ExampleClass("Hello");

a.Add( t );

//At this point, I would loop round the count of the ArrayList
b.Add( ((ExampleClass)a[0]).Clone() );

((ExampleClass)a[0]).m_Value = "World";
}

Yup, that would be fine - except you could just use a foreach on the
loop, rather than using Count explictly.

Of course, MemberwiseClone only gives a shallow copy too - you'll need
to clone in an appropriate way for each class. Note that casting to
ICloneable instead of ExampleClass would be more flexible.

Jon
 
S

Steven Blair

Now I am confused.
My testing appears to show a deep copy is taking place, so how can the
MemberwiseClone only give me a shallow copy?

This line of code:

((ExampleClass)a[0]).m_Value = "World";

Changes object a, but b is unchanged.

Good point regarding the casting. I have now updated that.

I double check the code, and I do appear to have 2 seperate classes
after using the Clone().
 
M

Marc Gravell

Because it clones the fields essentially as a memcopy operation. For
value-type fields (numerics etc) this is fine. However, for reference-types
(custom classes, or lists, collections, arrays etc) you will simply clone
the *pointer* to the actual class on the heap, so to get a true "deep" clone
you would have to keep going. Note that for immutable reference types (such
as string) you won't really notice the difference.

It is unfortunate that IClonable doesn't distinguish between shallow and
deep.

Marc
 
S

Steven Blair

oh, now I understand:

public class ExampleClass : ICloneable
{
public string m_Value;
public ArrayList m_Names; // :( shallow copy time

public ExampleClass(string s)
{
m_Value = s;
m_Names = new ArrayList();
}

public override string ToString()
{
return m_Value;
}

object ICloneable.Clone()
{
return this.Clone();
}

public virtual ICloneable Clone()
{
return this.MemberwiseClone() as ICloneable;
}
}

m_Names would be a shallow copy :(

This is realy not good. It seems extremely complicated to copy (deep)
structures in C#.

I think living with the slow structure construction time is the only
option for me at the moment.

Thanks for the help everyone.
 
S

Steven Blair

Is this how I would deep copy an ArrayList inside my custom class:

public class ExampleClass : ICloneable
{
public string m_Value;
public ArrayList m_Names;

public ExampleClass(string s)
{
m_Value = s;
m_Names = new ArrayList();
}

public override string ToString()
{
return m_Value;
}

object ICloneable.Clone()
{
return this.Clone();
}

public virtual ICloneable Clone()
{
ExampleClass x = this.MemberwiseClone() as ExampleClass;

//This is how I would Clone reference type?
x.m_Names = this.m_Names.Clone() as ArrayList;

return x as ICloneable;
}
}

This does seem to work, but would like to double check.

My custom class in the production software is mainly made up of string,
int, bool and enum. A few methods and an ArrayList. As far as I can see,
the ArrayList is the only object that would be a shallow copy?

Hopefully this is case closed this time!
 
C

Christopher Ireland

Steven Blair said:
This does seem to work, but would like to double check.

Try:
public class ExampleClass : ICloneable
{
public string m_Value;
public ArrayList m_Names;

public ExampleClass(string s, ArrayList a)
{
m_Value = s;
m_Names = a;
}

public ExampleClass() { }

public override string ToString()
{
return m_Value;
}

object ICloneable.Clone()
{
return this.Clone();
}

public virtual ExampleClass Clone()
{
ExampleClass result = new ExampleClass();
if (m_Names != null)
{
result.m_Names = new ArrayList();
foreach (object o in m_Names)
{
result.m_Names.Add(o); //again, if o are reference types you'll have to
make your own clone
}
}

return result;
}
}

Cheers!

Chris.
 
S

Steven Blair

Chris,

o is reference type.

I assumed the code I had in there would do the deep copy now?
 
C

Christopher Ireland

Steven Blair said:
Chris,

o is reference type.

I assumed the code I had in there would do the deep copy now?

Both reference types and value types derive from object.

ArrayList.Clone() produces a shallow copy. If you want a deep copy you'll
have to create another instance (memory reference) and copy the members
across, cloning the reference types as necessary.

Don't forget that shallow copy means referring to the same instance, whereas
a deep copy is a difference instance.
e.g.

ExampleClass one = new ExampleClass;
ExampleClass two = one;

both two and one refer to the same instance ("shallow").

ExampleClass one = new ExampleClass;
ExampleClass two = new ExampleClass;

one and two are different instances ("deep").

Cheers!

Chris.
 
C

Christopher Ireland

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top