1

I have a C# class called "SmallClass".

I have an existing list myList containing objects of type "SmallClass"

I want a deep clone of the list "myList". That is, deep Clone the containing list and deep clone the objects contained in the list.

How should I do this.

    public class SmallClass: ICloneable {

    public string str1;
    public string str2;
    public string str3;

     public SmallClass Clone() //This just deep clones 1 object of type "SmallClass"
            {
                MemoryStream m = new MemoryStream();
                BinaryFormatter b = new BinaryFormatter();
                b.Serialize(m, this);
                m.Position = 0;
                return (SRO)b.Deserialize(m);
            }

      public override equals(Object a)
        {
                return Object.Equals(this.str1 && a.str1);
            }
    }

    public class AnotherClass
    {
           SomeCode();
           List<SmallClass> myList = new List<SmallList>();  //myList is initialized.


           // NOW I want to deep clone myList. deep Clone the containing list and deep clone the objects contained in the list.

         List<SmallClass> newList = new List<SmallClass>();
      foreach(var item in myList)
        {
           newList.Add((SmallClass)item.Clone());
        }       

}

1
  • What's going on here? return (a.boolean1 && a.boolean1); Commented May 16, 2012 at 19:07

3 Answers 3

6

Warning: The BinaryFormatter type is dangerous when used with untrusted input. Whilst the usage below should be safe, Microsoft recommend avoiding BinaryFormatter altogether due to its potential for misuse, and will remove it from .NET 7–8. Consider using another serializer or approach for your deep clones.

First off, you can define a utility method for deep-cloning any object (root):

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

If you want to deep-clone myList, all you need to do is pass it as parameter to the method above:

List<SmallClass> myListClone = DeepClone(myList);

The most important consideration you need to pay attention to is that all your classes must be marked as serializable, typically through the [SerializableAttribute].

[SerializableAttribute]
public class SmallClass
{
    // …
}
Sign up to request clarification or add additional context in comments.

11 Comments

the ICloneable interface class the interface method "Clone" - so if i were to inherit from ICloneable - I will call the method "Clone" instead of DeepClone()
BinaryFormatter would take care of deep-cloning your entire object graph, without requiring you to stay calling the Clone methods progressively. The only constraint is that your custom classes must be marked as [SerializableAttribute].
Do you mean that just calling "DeepClone(myList)" will automatically take care of deep copying any nested object references in myList?
This solution is now considered insecure and should not be used for any productive work. aka.ms/binaryformatter
@Exitare: Fair point. I've added a warning to the top of the answer.
|
4

Your SmallClass needs to implement the ICloneable interface. Then copy every element using the Clone() method.

List<SmallClass> newList = new List<SmallClass>();
foreach(var item in myList)
{
    newList.Add((SmallClass)item.Clone());
}

3 Comments

Will I need to provide a new implementation for the implementation of clone inherited from ICloneable in "SmallClass"
Yes, ICloneable is just an interface, which means that it has no implementation whatsoever. You have to implement the Clone() method yourself. That is what I mean when I say "Implement the interface."
Ok. I add your suggestion to my original post. Does the code seem to lack anything functionally? SmallClass does not contain class members I will not need to think about cloning these.
0

There are a few ways of creating a deep copy which include serialization and using Object.MemberwiseClone Method (). Since an example of using serialization is already available here, I have an approach with using "MemberwiseClone".

NOTES: Recursive, Platform: .NETStandard2.0

        /// <summary>
        /// Returns a deep copy of an object.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <returns></returns>
        public static T DeepClone<T>(this T source) where T : class
        {
            if(source == null) return null;

            if(source is ICollection<object> col)
            {
                return (T)DeepCloneCollection(col);
            }
            else if(source is IDictionary dict)
            {
                return (T)DeepCloneDictionary(dict);
            }

            MethodInfo method = typeof(object).GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance);
            T clone = (T)method.Invoke(source, null);

            foreach(FieldInfo field in source.GetType().GetRuntimeFields())
            {
                if(field.IsStatic) continue;
                if(field.FieldType.GetTypeInfo().IsPrimitive) continue;

                object sourceValue = field.GetValue(source);
                field.SetValue(clone, DeepClone(sourceValue));
            }

            return clone;
        }

        private static ICollection<object> DeepCloneCollection(ICollection<object> col)
        {
            object[] arry = (object[])Activator.CreateInstance(col.GetType(), new object[] { col.Count });

            for(int i = 0; i < col.Count; i++)
            {
                object orig = col.ElementAt(i);
                object cln = DeepClone(orig);

                arry[i] = cln;
            }

            return arry;
        }

        private static IDictionary DeepCloneDictionary(IDictionary dict)
        {
            IDictionary clone = (IDictionary)Activator.CreateInstance(dict.GetType());

            foreach(object pair in dict)
            {
                object key = pair.GetValueOf("Key");
                object original = pair.GetValueOf("Value");

                clone.Add(key, original.DeepClone());
            }

            return clone;
        }

        public static dynamic GetValueOf<T>(this T value, string property)
        {
            PropertyInfo p = value.GetType().GetTypeInfo().GetProperty(property);

            if(p != null && p.CanRead)
            {
                dynamic val = p.GetValue(value);

                return val;
            }

            return Activator.CreateInstance(p.PropertyType); //Property does not have  value, return default
        }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.