user1216456
user1216456

Reputation:

deep clone a List of object in c#

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());
        }       

}

Upvotes: 1

Views: 6741

Answers (3)

Douglas
Douglas

Reputation: 54877

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
{
    // …
}

Upvotes: 6

Jeffrey Ferreiras
Jeffrey Ferreiras

Reputation: 271

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
        }

Upvotes: 0

Breealzibub
Breealzibub

Reputation: 8095

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());
}

Upvotes: 4

Related Questions