18

I am trying to create a deep clone of an object using the following method.

    public static T DeepClone<T>(this T target)
    {
        using (MemoryStream stream = new MemoryStream())
        {
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, target);
            stream.Position = 0;
            return (T)formatter.Deserialize(stream);
        }
    } 

This method requires an object which is Serialized i.e. an object of a class who is having an attribute "Serializable" on it. I have a class which is having attribute "DataContract" on it but the method is not working with this attribute. I think "DataContract" is also a type of serializer but maybe different than that of "Serializable".

Can anyone please give me the difference between the two? Also please let me know if it is possible to create a deepclone of an object with just 1 attribute which does the work of both "DataContract" and "Serializable" attribute or maybe a different way of creating a deepclone?

Please help!

JasonMArcher
  • 14,195
  • 22
  • 56
  • 52
samar
  • 5,021
  • 9
  • 47
  • 71

3 Answers3

28

Serializable is needed for the BinaryFormatter to work.

DataContract and the DataMember attribute are used with the DataContractSerializer.

You can decorate a class with attributes for both serializers.

Oded
  • 489,969
  • 99
  • 883
  • 1,009
  • Thanks for the response @Oded. Just one question here. This class is being used in my WCF Service. I am consuming this service in my consumer project by adding the service reference of the service. Whenever there is a change in the Service I need to update the service reference. When I use both the attributes and then update the service, .net creates 2 properties with the same name in the Reference.cs class of the Service Reference. 1 each for "DataMember" and "Serializable" attribute. This creates a build error. Any work around for this? – samar Dec 28 '10 at 11:35
  • @samar - As far as I know, `Serializable` will be ignored if `DataContract` is used. I have never heard of the problem you have. – Oded Dec 28 '10 at 11:46
6

DataContract is used in WCF hence .NET 3.0+. In .net 2.0 or lower there is not DataContract, DataMember attribute, only Serializable.

As Oded said, if you want to use BinaryFormatter you have to decorate the type with Serializable.

Liviu Mandras
  • 6,540
  • 2
  • 41
  • 65
3

I once did some inspection to an object structure via Reflection to find all assemblies required for deserialization and serialize them alongside for bootstrapping.

With a bit of work one could build a similar method for deep copying. Basically you need a recursive method that carrys along a Dictionary to detect circular references. Inside the method you inspect all fields about like this:

private void InspectRecursively(object input,
    Dictionary<object, bool> processedObjects)
{
  if ((input != null) && !processedObjects.ContainsKey(input))
  {
    processedObjects.Add(input, true);

    List<FieldInfo> fields = type.GetFields(BindingFlags.Instance |
        BindingFlags.Public | BindingFlags.NonPublic );
    foreach (FieldInfo field in fields)
    {
      object nextInput = field.GetValue(input);

      if (nextInput is System.Collections.IEnumerable)
      {
        System.Collections.IEnumerator enumerator = (nextInput as
            System.Collections.IEnumerable).GetEnumerator();

        while (enumerator.MoveNext())
        {
          InspectRecursively(enumerator.Current, processedObjects);
        }
      }
      else
      {
        InspectRecursively(nextInput, processedObjects);
      }
    }
  }
}

To get it working you need to add an output object and something like System.Runtime.Serialization.FormatterServices.GetUninitializedObject(Type type) to create the most shallowest copy (even without copying references) of each field's value. Finally you can set each field with something like field.SetValue(input, output)

However this implementation does not support registered event handlers, which is _un_supported by deserializing, too. Additionally each object in the hierarchy will be broken, if its class' constructor needs to initialize anything but setting all fields. The last point only work with serialization, if the class has a respective implementation, e.g. method marked [OnDeserialized], implements ISerializable,... .

No answer
  • 907
  • 1
  • 9
  • 10