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


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;

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.
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:

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#

 Buy Now From Amazon


 Chapter List

  1. Why C#?

    I Strong Typing & Type Safety
  2. Strong Typing
    Why Strong Typing
  3. Value & Reference
  4.    Extract Value And Reference 
  5. Structs & Classes
    Structs & Classes 
  6. Inheritance
  7. Interfaces & Multiple Inheritance
    Extract Interface ***NEW!
  8. Controlling Inheritance

    II Casting & Generics
  9. Casting - The Escape From Strong Typing
  10. Generics
  11. Advanced Generics
  12. Anonymous & Dynamic

    III Functions
  13. Delegates
  14. Multicast Delegates
  15. Anonymous Methods, Lambdas & Closures

    IV Async
  16. Threading, Tasks & Locking
  17. The Invoke Pattern
  18. Async Await
  19. The Parallel For

    V Data - LINQ, XML & Regular Expressions
  20. The LINQ Principle
  21. XML
  22. LINQ To XML
  23. Regular Expressions

    VI Unsafe & Interop
  24. Interop
  25. COM
  26. Custom Attributes
  27. Bit Manipulation
  28. Advanced Structs
  29. Pointers 

Extra Material








or email your comment to:



Last Updated ( Thursday, 19 November 2015 )