Copy Reference Type?

  • Thread starter Thread starter Brett Wickard
  • Start date Start date
B

Brett Wickard

Ok, this should be simple, but how do I copy a reference type by value?

Meaning

SomeComplexObject s1 = new SomeComplexObject ();
SomeComplexObject s2 = new SomeComplexObject ();
s1.color = "red";
s2 = s1;
s2.color = "blue";

I want to copy s1 to s2 and have them point to different points in memory,
so that if I change s2.color I won't change s1.color... How do I do that?
The actual object has like 30-ish parameters so I'm hoping I don't have to
assign each one separately!
 
You can write your own Copy or Clone method to do this, but that's what
you'll have to do.

--
HTH,

Kevin Spencer
Microsoft MVP
Chicken Salad Surgery

Orange you bland I stopped splaying bananas?
 
Brett,

There is no standard way to do this. The framework defines an IClonable
interface which is used for things like this. However, it doesn't say
whether or not you should do a deep copy or a shallow copy of the object.

The simplest way to do this would be to implement IClonable, and then
have it return what MemberwiseClone (a protected method on Object) returns.
However, this is only one layer deep. If you have object references in your
object which is being copied, then those objects are not copied, but rather,
their references.

If you want a dirty way to do this, and the object is marked as
serializable, then you can serialize the object to a memory stream, and then
deserialize it back, and you have a deep copy.

Hope this helps.
 
Brett Wickard said:
Ok, this should be simple, but how do I copy a reference type by value?

Meaning

SomeComplexObject s1 = new SomeComplexObject ();
SomeComplexObject s2 = new SomeComplexObject ();
s1.color = "red";
s2 = s1;
s2.color = "blue";

I want to copy s1 to s2 and have them point to different points in memory,
so that if I change s2.color I won't change s1.color... How do I do that?
The actual object has like 30-ish parameters so I'm hoping I don't have to
assign each one separately!

Yes, you have to write a copy method returning new instance of your class
manually. You can make a shallow copy usign the MemberwiseClone() method
(inherited from object) but the fields of the new instance will still
reference the same objects in memory.

A possible hack to perform a deep copy is to serialize and deserialize the
instance.
 
Brett Wickard said:
Ok, this should be simple, but how do I copy a reference type by value?

I religiously implement IClonable in all my classes where possible, along
with a copy constructor. The consumer of the class can then use Clone or
the copy constructor, as desired.

-- Alan
 
Thanks everyone for the answers... Just a question about the copy
contructor, it seems like that could cause a bug --- meaning that if I add a
new property to my class and forget to include it in the copy contructor,
that one property being referenced instead of copied probably wouldn't show
up for quite a while... do you have a good methodology that mitigates that
risk (other than not being forgetful :) )?
 
Brett,

You might want to implement a Reflection-based property to do this.
Basically, cycle through the fields on a class, digging into sub classes,
etc, etc, setting the values.

And I have say that I think that Alan's solution is not a good general
purpose solution. Implementing copy constructors (which really don't have
any special significance in C# unlike the way they did in C++) and IClonable
on all of your classes might not give you the best solution (you have to
ask, why are you doing this?).
 
Brett Wickard said:
Thanks everyone for the answers... Just a question about the copy
contructor, it seems like that could cause a bug --- meaning that if I add
a new property to my class and forget to include it in the copy
contructor, that one property being referenced instead of copied probably
wouldn't show up for quite a while... do you have a good methodology that
mitigates that risk (other than not being forgetful :) )?

Right you are about a potential for bugs...

Since the copy constructor is implemented using ICloneable.Clone(), the
issue is implementing Clone(). Couple things. If your object is flat, then
you can implement Clone using MemberwiseClone(). If you have a nested
object, and it is serializable, then there is an in-memory deepcopy routine
you can use to copy it. Google the net or the C# newsgroup to find them,
they are around. Nicholas also mentions reflection.

The final step of course, is to write unit tests of your clone routine. You
do do unit testing, right?

-- Alan
 
Interesting stuff to digest --- thanks again for taking the time to respond!

Nicholas Paldino said:
Brett,

You might want to implement a Reflection-based property to do this.
Basically, cycle through the fields on a class, digging into sub classes,
etc, etc, setting the values.

And I have say that I think that Alan's solution is not a good general
purpose solution. Implementing copy constructors (which really don't have
any special significance in C# unlike the way they did in C++) and
IClonable on all of your classes might not give you the best solution (you
have to ask, why are you doing this?).


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Brett Wickard said:
Thanks everyone for the answers... Just a question about the copy
contructor, it seems like that could cause a bug --- meaning that if I
add a new property to my class and forget to include it in the copy
contructor, that one property being referenced instead of copied probably
wouldn't show up for quite a while... do you have a good methodology that
mitigates that risk (other than not being forgetful :) )?
 
Thanks for the response. Since I have no idea what unit testing is, I have
some reading to do!
 
Brett Wickard said:
Thanks for the response.

I mangled my description in my last post, let me give an example of
Clone/Copy constructor.

namespace MyNamespace {
public class A : System.ICloneable {
private int _i, _j;

// Default constructor.
public A() {
_i = _j = 0;
return;
}

// Copy constructor.
public A(A A) {
_i = A._i;
_j = A._j;
return;
}

object System.ICloneable.Clone() {
return new A(this);
}
}

public class B : System.ICloneable {
private A _A;
private int _i;

// Default constructor.
public B() {
_i = 0;
return;
}

// Copy constructor. Deep copy.
public B(B B) {
_A = new A(B._A); // Uses copy constructor, this is the deep
copy.
_i = B._i;
return;
}

object System.ICloneable.Clone() {
return new B(this);
}
}
}

Since I have no idea what unit testing is, I have some reading to do!

See http://nunit.org/


-- Alan
 
Nicholas Paldino said:
Implementing copy constructors (which really don't have any special
significance in C# unlike the way they did in C++)

Using a copy constructor allows me to write as in (i):

Assuming,
A Obj1, Obj2;
Obj1 = new A();

Then,
Obj2 = new A(Obj1); // (i)

Or,
Obj2 = Obj1.Clone() As A // (ii)

Or,
Obj2 = (A) Obj1.Clone(); // (iii)


You can argue about which is better. But the point is there is next to no
cost in the class to implement Clone() if you already have a copy
constructor.

-- Alan
 
It's a valid point, but you are ignoring two important questions:

a) How deep is too deep?

and

b) What is the pratical application of having a copy constructor on
everything, as well as an implementation of IClonable.

There is not one standard answer for either of these, and the questions
are very closely intertwined. It's something the OP has to answer for
himself, depending on the semantics that he wants in his program.
 
Nicholas Paldino said:
a) How deep is too deep?

In answer to the question, "how do I make a deep copy of an object?", then
disciplined use of copy constructors and/or ICloneable works. Of course,
there is always the "You (client) may think you want this, but you really
don't" issue that the class designer needs to consider.
b) What is the pratical application of having a copy constructor on
everything, as well as an implementation of IClonable.

This is a discipline that I always follow for my own classes. It gets
tricky when you need to implement a clone on a class that wraps another one
that provides no equivalent, like some framework classes, but in almost all
cases it is doable, if it makes sense to copy the object at all.


As an aside, what I posted earlier doesn't compile. After posting I
realized that C# requires casting to ICloneable:

Obj2 = ((System.ICloneable) Obj1).Clone() As A // (ii)
or
Obj2 = (A) ((System.ICloneable) Obj1).Clone(); // (iii)

which makes

Obj2 = new A(Obj1); // (i)

look even better.

-- Alan
 
As an aside, what I posted earlier doesn't compile. After posting I
realized that C# requires casting to ICloneable:

Obj2 = ((System.ICloneable) Obj1).Clone() As A // (ii)
or
Obj2 = (A) ((System.ICloneable) Obj1).Clone(); // (iii)

which makes

Obj2 = new A(Obj1); // (i)

look even better.

-- Alan

Just a side note: the cast is required only if the interface is implemented
explicitly.
 
Lebesgue said:
Just a side note: the cast is required only if the interface is
implemented explicitly.

Thanks. To be honest, I rarely, if ever, use Clone() directly since I have
the copy constructors. Again, it is up to the preference of the consumer.

-- Alan
 

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

Back
Top