Article purpose
This article will illustrate how to create deep copies of an object by making use of the NetDataContractSerializer implemented in the form of an extension method with generic type support.
Sample source code
This article is accompanied by a sample source code Visual Studio project which is available for download here.
Shallow Copy and Deep Copy
When creating a copy of an object in memory, the type of copy can be described as either a shallow copy or a deep copy. The Object class defines the MemberwiseClone method, which performs a bit by bit copy of an object’s value type members. In the case of reference type members the MemberwiseClone method will create a copy of the reference, but not a copy of the Object being referenced. Creating a copy of an Object using the MemberwiseClone method will thus result in copies and the original Object still referencing the same member Object in memory when that Object is a reference type. The MemberwiseClone method performs a shallow copy when invoked.
A deep copy of an Object results in copies and the original Object not referencing the same reference type member Object in memory.
This article is a follow up article on: C# How to: Deep copy objects using Binary Serialization. When using Binary Serialization objects being serialized have to be decorated with any number of attributes which aid serialization and deserialization. An object’s definition has to at the very least specify the Serializable attribute, if not attempting serialization results in a runtime exception.
The advantage of implementing deep copy operations by making use of a NetDataContractSerializer can be argued around not having to specify serialization attributes. Although, as is the case with binary serialization, only objects that define a default/parameter less public constructor can be serialized without specifying any additional attributes.
Example custom data type
The code snippet listed below illustrates several user/custom defined data types. Notice the complete absence of any code attributes, as usually required for successful serialization/deserialization. Also pay attention to the private member variables, being an enum and user defined reference type defined towards the end of this snippet.
For the sake of convenience I overload the ToString() method, returning a string representation of an object’s member values.
public class CustomDataType { private CustomEnum enumMember = CustomEnum.EnumVal1; private ExampleReferenceType referenceType = new ExampleReferenceType();
public void RefreshReferenceType() { referenceType.Refresh(); }
private int intMember = 0; public int IntMember { get { return intMember; } set { intMember = value; } }
private string stringMember = String.Empty; public string StringMember { get { return stringMember; } set { stringMember = value; } }
private DateTime dateTimeMember = DateTime.MinValue; public DateTime DateTimeMember { get { return dateTimeMember; } set { dateTimeMember = value; } }
public override string ToString() { return "IntMember: " + IntMember + ", DateTimeMember: " + DateTimeMember.ToString() + ", StringMember: " + stringMember + ", EnumMember: " + enumMember.ToString() + ", ReferenceType: " + referenceType.ToString(); }
public void SetEnumValue(CustomEnum enumValue) { enumMember = enumValue; } }
public class ExampleReferenceType { private DateTime createdDate = DateTime.Now;
public void Refresh() { createdDate = DateTime.Now; }
public override string ToString() { return createdDate.ToString("HH:mm:ss.fff"); } }
public enum CustomEnum { EnumVal1 = 2, EnumVal2 = 4, EnumVal3 = 8, EnumVal4 = 16, }
The DeepCopy method – Implementation as an extension method with generic type support
Extension method architecture enables developers to create methods which, from a syntactic and implementation point of view appear to be part of an existing data type. Extension methods create the perception of being updates or additions, literarily extending a data type as the name implies. Extension methods do not require access to the source code of the particular types being extended, nor does the implementation thereof require recompilation of the referenced types.
This article illustrates a combined implementation of extension methods extending the functionality of generic types. The following code snippet provides the extension method definition.
public static class ExtObject { public static T DeepCopy<T>(this T objectToCopy) { MemoryStream memoryStream = new MemoryStream();
NetDataContractSerializer netFormatter = new NetDataContractSerializer();
netFormatter.Serialize(memoryStream, objectToCopy);
memoryStream.Position = 0; T returnValue = (T)netFormatter.Deserialize(memoryStream);
memoryStream.Close(); memoryStream.Dispose();
return returnValue; } }
The DeepCopy method is defined as an extension method by virtue of being a static method of a static class and by specifying the this keyword in its parameter definition.
DeepCopy additionally defines the generic type <T> which determines the return value’s type and the type of the parameter objectToCopy.
The method body creates an instance of a MemoryStream object and an object instance of type NetDataContractSerializer. When the Serialize method is invoked the Xml representation of the objectToCopy parameter is written to the specified MemoryStream. In a similar fashion Deserialize is invoked next, reading the Xml representation from the specified MemoryStream. The object returned is cast to the same type as the object originally serialized.
Note: From MSDN documentation:
Serializes and deserializes an instance of a type into XML stream or document using the supplied .NET Framework types.
The NetDataContractSerializer differs from the DataContractSerializer in one important way: the NetDataContractSerializer includes CLR type information in the serialized XML, whereas the DataContractSerializer does not. Therefore, the NetDataContractSerializer can be used only if both the serializing and deserializing ends share the same CLR types.
In the scenario illustrated it can be considered safe to use NetDataContractSerializer since objects being serialized are only persisted to memory for a few milliseconds and then deserialized back to an object instance.
The implementation
The DeepCopy method illustrated above appears as a member method to the CustomDataType class created earlier.
static void Main(string[] args) { CustomDataType originalObject = new CustomDataType(); originalObject.DateTimeMember = DateTime.Now; originalObject.IntMember = 42; originalObject.StringMember = "Some random string";
CustomDataType deepCopyObject = originalObject.DeepCopy(); deepCopyObject.DateTimeMember = DateTime.MinValue; deepCopyObject.IntMember = 123; deepCopyObject.StringMember = "Something else..."; deepCopyObject.SetEnumValue(CustomEnum.EnumVal3); deepCopyObject.RefreshReferenceType();
Console.WriteLine("originalObject: "); Console.WriteLine(originalObject.ToString()); Console.WriteLine();
Console.WriteLine("deepCopyObject: "); Console.WriteLine(deepCopyObject.ToString()); Console.WriteLine();
Console.WriteLine("Press any key..."); Console.ReadKey(); }
The code snippet listed above is a console application which implements the DeepCopy extension method on objects of type CustomDataType. Modifying the member properties of the second object instance will not result in the first object instance properties being modified.
Filed under: C#, Code Samples, Extension Methods, Generics, How to, Microsoft, New Version, Opensource, Tip, XML, XML Serialization Tagged: Binary serialization, C#, Code Sample, Deep Copy, deserialize to object, Extension Methods, Generics, How to, MemberwiseClone, NetDataContractSerializer, serialize to xml, Shallow Copy, Xml, Xml Deserialization, Xml Serialization