Deep C# - Passing Parameters
Written by Mike James   
Thursday, 19 November 2015
Article Index
Deep C# - Passing Parameters
Pass by reference
Boxing
Making Clones

coverCsharp

Making Clones

If all you require is a shallow clone then you can simply use the MemberwiseClone which is inherited from object.

This is a protected non-virtual method that reduces its usefulness a little.

To use it you simply add a clone or copy method to your reference type:

public class PointR
{
 public int x;
 public int y;
 public PointR clone()
 {
  return (PointR) MemberwiseClone();
 }
}

If you now use:

PointR a = new PointR();
PointR b = a;
b.x = 20;

you will discover, as always, that a.x has changed to be 20.

However if you use:

PointR a = new PointR();
PointR b = a.clone();
b.x = 20;

you will discover that a.x is still set to its default value of zero. The clone method has created a completely new object on the heap for b to reference.

To see that this new object is a shallow copy is fairly easy - simply add a field that is a reference type. a StringBuilder for example:

public class PointR
{
 public int x;
 public int y;
 public StringBuilder z;
 public PointR clone()
 {
  return (PointR) MemberwiseClone();
 }
}

Now when you clone the object you clone the reference to StringBuilder but not the StringBuilder object it references:

PointR a = new PointR();
a.z=new StringBuilder(“test1");
PointR b = a.clone();
b.x = 20;
b.z.Append(“test2");

Notice the way that we have to create a new StringBuilder object before we can use the field as discussed in the previous Chapter a reference type has to be initialise in this way before use.

If you examine the contents of b.z and a.z you will discover that both are set to “test1test2" but b.x and a.x  still hold different values.

You might be wondering why StringBuilder was used as an example of a reference type and not just String?

The answer is that String is an immutable reference type which means that each time you modify a String a new object is created. If you change the example to use a string you will find that although only the reference to the string is cloned assigning a new value to b.z creates a new String object leaving a.z still referencing the old version.

Thus an immutable object behaves as if it has been passed by value even when its really been passed by reference.

More on Strings and immutable objects later.

The Iclonable interface was introduced to provided a standard way of copying object but this has mostly been ignored because of the problem of knowing if its single member, the clone method, was to perform a shallow or a deep clone. In addition the clone method returned an object type forcing the user to caste to the correct type before using the clone. In addition by its very nature cloning can cause problems in derived classes and it needs to be treated with caution.

If you want to create a deep clone then the simplest solution is to use the serialization services provided by the framework. Serialization is intended to be used to convert an object into binary, perhaps with a choice of formatting, so that it can be saved on disk or transmitted to another location. Deseralization can then be used to read the stream of bits and reconstruct the object. It might sound complicated but the framework really does all the work for us. The only complication is that we need a stream as well as a formatter object to make it all work and hence need two additional using statements:

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

Any class that you are going to serialize has to be marked with the serializable attribute. This is simply a check that you have considered the implications of attempting to serialize the class. In this case we make the PointR class as serializable:

[Serializable]
public class PointR

Now we can make a clone by first serializing the original object to a memory stream:

BinaryFormatter BF = new BinaryFormatter();
MemoryStream MS=new MemoryStream(1000);
BF.Serialize(MS, a);

You could use a file stream if the object is very large but this would clearly slow things down. At this point we have a binary representation of all of the non-static fields in the object that a references. The serialization is a deep copy because the BinaryFormatter walks the object graph following references and serializing any objects it finds - assuming that these are also marked as serializable. If an object that isn’t marked as serializable is encountered then the serialization fails.

To deserialize the stream we simply reset it to the start and use the Deserialize method:

MS.Position = 0;
PointR c = (PointR) BF.Deserialize(MS);

At this point we have a deep clone of the original object referenced by a on referenced by c. Any changes to the fields of c, including the StringBuilder in z, have no effect on a.

Of course it makes sense to package the serialize/deserialize in a method that is part of the class being serialized. For example, if we add the following method to PointR:

public PointR deepClone()
{
 BinaryFormatter BF = new BinaryFormatter();
 MemoryStream MS = new MemoryStream(1000);
 BF.Serialize(MS, this);
 MS.Position = 0;
 return (PointR)BF.Deserialize(MS);
}

a deep clone of a can be created using:

PointR c = a.deepClone();

Notice the use of this within the method to ensure that it is the current object that is cloned.

You can control the serialisation process by marking fields that should not be serialized as [NonSerializedAttribute ]. For even more control you can implement the ISerializable interface to completely customise the conversion process.

Of course there are other uses for serilization and it is worth knowing that there is also a SoapFormatter class that can be used in place of the BinaryFormatter.

 

Deep C#

 front

 Chapter List

  1. Value And Reference
  2. Dynamic C#
  3. Passing Parameters
  4. Inheritance ***NEW
  5. Casting – the escape from strong typing
  6. Controlling Inheritance
  7. Delegates
  8. Multicast delegates and events
  9. Anonymous Methods, Lambdas And Closures
  10. Take Exception To Everything
  11. What's The Matter With Pointers?
  12. Generics
  13. Structs
  14. The LINQ Principle
  15. Regular Expressions in depth
  16. Bit Manipulation
  17. Async
  18. The Parallel For
Multicast delegates and events
Tuesday, 25 May 2010
Article Index
Multicast delegates and events
Events
Generic Events

Multicast delegates are useful in their own right but they also form the basis on which the C# event system is built. We take a close look at how they work and how to use them. For example, did you know you could add and subtract delegates?

square

 



 

Comments




or email your comment to: comments@i-programmer.info

 

 



Last Updated ( Thursday, 19 November 2015 )