8

I am working on some code that is written in C#. In this app, I have a custom collection defined as follows:

public class ResultList<T> : IEnumerable<T>
{
  public List<T> Results { get; set; }
  public decimal CenterLatitude { get; set; }
  public decimal CenterLongitude { get; set; }
}

The type used by Results are one of three custom types. The properties of each of the custom types are just primitive types (ints, strings, bools, int?, bool?). Here is an example of one of the custom types:

public class ResultItem
{
  public int ID { get; set; }
  public string Name { get; set; }
  public bool? isLegit { get; set; }
}

How do I perform a deep copy of a ResultList object that I've created. I found this post: Generic method to create deep copy of all elements in a collection. However, I can't figure out how to do it.

3
  • What have you tried? What error message you got? Jon Skeet's code you found simply works as far as I can see. Commented Jun 17, 2012 at 20:06
  • Shallow or deep copy? stackoverflow.com/questions/11073196/… Commented Jun 17, 2012 at 20:07
  • Why are you and the OP of this queston seeming to use the exact same data structure in your example? Commented Jun 17, 2012 at 20:21

5 Answers 5

16

The approach involving the least coding effort is that of serializing and deserializing through a BinaryFormatter.

You could define the following extension method (taken from Kilhoffer’s answer):

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

…and then just call:

ResultList<T> clone = DeepClone(original);
Sign up to request clarification or add additional context in comments.

Comments

9

One of the reasons why your ResultList class won't work with Jon Skeet's example is because it does not implement the ICloneable interface.

Implement ICloneable on all the classes that you need cloned, e.g.

public class ResultItem : ICloneable
{
  public object Clone()
  {
    var item = new ResultItem
                 {
                   ID = ID,
                   Name = Name,
                   isLegit = isLegit
                 };
    return item;
  }
}

And also on ResultList:

public class ResultList<T> : IEnumerable<T>, ICloneable where T : ICloneable
{
  public List<T> Results { get; set; }
  public decimal CenterLatitude { get; set; }
  public decimal CenterLongitude { get; set; }

  public object Clone()
  {
    var list = new ResultList<T>
                 {
                   CenterLatitude = CenterLatitude,
                   CenterLongitude = CenterLongitude,
                   Results = Results.Select(x => x.Clone()).Cast<T>().ToList()
                 };
    return list;
  }
}

Then to make a deep copy of your object:

resultList.clone();

Comments

4

Expanding on @Georgi-it, I had to modify his code to handle properties whose type inherits List:

public static class ObjectCloner {
    public static T Clone<T>(object obj, bool deep = false) where T : new() {
        if (!(obj is T)) {
            throw new Exception("Cloning object must match output type");
        }

        return (T)Clone(obj, deep);
    }

    public static object Clone(object obj, bool deep) {
        if (obj == null) {
            return null;
        }

        Type objType = obj.GetType();

        if (objType.IsPrimitive || objType == typeof(string) || objType.GetConstructors().FirstOrDefault(x => x.GetParameters().Length == 0) == null) {
            return obj;
        }

        List<PropertyInfo> properties = objType.GetProperties().ToList();
        if (deep) {
            properties.AddRange(objType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic));
        }

        object newObj = Activator.CreateInstance(objType);

        foreach (var prop in properties) {
            if (prop.GetSetMethod() != null) {
                var proceed = true;
                if (obj is IList) {
                    var listType = obj.GetType().GetProperty("Item").PropertyType;
                    if (prop.PropertyType == listType) {
                        proceed = false;
                        foreach (var item in obj as IList) {
                            object clone = Clone(item, deep);
                            (newObj as IList).Add(clone);                               
                        }                           
                    }                       
                }

                if (proceed) {
                    object propValue = prop.GetValue(obj, null);
                    object clone = Clone(propValue, deep);
                    prop.SetValue(newObj, clone, null);
                }                   
            }
        }

        return newObj;
    }
}

2 Comments

This code is wrong: for deep coping a string you way use "return String.Copy(obj as string);"
@sborfedor String.Copy is obsolete. The string is immutable anyways. The only way to modify the same reference is with unsafe code. In that case you probably won't copy-paste this solution anyways.
2

Here is something that I needed and wrote, it uses reflection to copy every property (and private ones if specified)

public static class ObjectCloner
{
    public static T Clone<T>(object obj, bool deep = false) where T : new()
    {
        if (!(obj is T))
        {
            throw new Exception("Cloning object must match output type");
        }

        return (T)Clone(obj, deep);
    }

    public static object Clone(object obj, bool deep)
    {
        if (obj == null)
        {
            return null;
        }

        Type objType = obj.GetType();

        if (objType.IsPrimitive || objType == typeof(string) || objType.GetConstructors().FirstOrDefault(x => x.GetParameters().Length == 0) == null)
        {
            return obj;
        }

        List<PropertyInfo> properties = objType.GetProperties().ToList();
        if (deep)
        {
            properties.AddRange(objType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic)); 
        }

        object newObj = Activator.CreateInstance(objType);

        foreach (var prop in properties)
        {
            if (prop.GetSetMethod() != null)
            {
                object propValue = prop.GetValue(obj, null);
                object clone = Clone(propValue, deep);
                prop.SetValue(newObj, clone, null);
            }
        }

        return newObj;
    }
}

3 Comments

Handle objects that inherit List<T>: I'll just create a second answer since code in the comments suck.
This code is wrong: for deep coping a string you way use "return String.Copy(obj as string);"
@sborfedor String.Copy is obsolete. The string is immutable anyways. The only way to modify the same reference is with unsafe code. In that case you probably won't copy-paste this solution anyways.
1

For deep coping of an object you can use this code:

public static T DeepCopy<T>(T obj) {
    var str = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
    var ret = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(str);
    return ret;
}

1 Comment

Newtonsoft.Json.JsonSerializationException error when tried this code on a complex model

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.