Deep copy of List<T>

I'm trying to make a deep copy of a generic list, and am wondering if there is any other way then creating the copying method and actually copying over each member one at a time. I have a class that looks somewhat like this:

public class Data
{            
    private string comment;
    public string Comment
    {
        get { return comment; }
        set { comment = value; }
    }

    private List<double> traceData;
    public List<double> TraceData
    {
        get { return traceData; }
        set { traceData = value; }
    }
}

And I have a list of the above data, i.e List<Data>. What I'm trying to do is plot a trace data of the subset of List onto a graph, possibly with some scaling or sweeping on the data. I obviously don't need to plot everything in the list because they don't fit into the screen.

I initially tried getting the subset of the list using the List.GetRange() method, but it seems that the underneath List<double> is being shallow copied instead of deep copied. When I get the subset again using List.GetRange(), I get previously modified data, not the raw data retrieved elsewhere.

Can anyone give me a direction on how to approach this? Thanks a lot.


The idiomatic way to approach this in C# is to implement ICloneable on your Data, and write a Clone method that does the deep copy (and then presumably a Enumerable.CloneRange method that can clone part of your list at once.) There isn't any built-in trick or framework method to make it easier than that.

Unless memory and performance are a real concern, I suggest that you try hard to redesign it to operate on immutable Data objects, though, instead. It'll wind up much simpler.


You can try this

    public static object DeepCopy(object obj)
    {
        if (obj == null)
            return null;
        Type type = obj.GetType();

        if (type.IsValueType || type == typeof(string))
        {
            return obj;
        }
        else if (type.IsArray)
        {
            Type elementType = Type.GetType(
                 type.FullName.Replace("[]", string.Empty));
            var array = obj as Array;
            Array copied = Array.CreateInstance(elementType, array.Length);
            for (int i = 0; i < array.Length; i++)
            {
                copied.SetValue(DeepCopy(array.GetValue(i)), i);
            }
            return Convert.ChangeType(copied, obj.GetType());
        }
        else if (type.IsClass)
        {

            object toret = Activator.CreateInstance(obj.GetType());
            FieldInfo[] fields = type.GetFields(BindingFlags.Public |
                        BindingFlags.NonPublic | BindingFlags.Instance);
            foreach (FieldInfo field in fields)
            {
                object fieldValue = field.GetValue(obj);
                if (fieldValue == null)
                    continue;
                field.SetValue(toret, DeepCopy(fieldValue));
            }
            return toret;
        }
        else
            throw new ArgumentException("Unknown type");
    }

Thanks to DetoX83 article on code project.


If IClonable way is too tricky for you. I suggest converting to something and back. It can be done with BinaryFormatter or a Json Converter like Servicestack.Text since it is the fastest one in .Net.

Code should be something like this:

MyClass mc = new MyClass();
string json = mc.ToJson();
MyClass mcCloned = json.FromJson<MyClass>();

mcCloned will not reference mc.


The most easiest (but dirty) way is to implement ICloneable by your class and use next extension method:

public static IEnumerable<T> Clone<T>(this IEnumerable<T> collection) where T : ICloneable
{
    return collection.Select(item => (T)item.Clone());
}

Usage:

var list = new List<Data> { new Data { Comment = "comment", TraceData = new List { 1, 2, 3 } };
var newList = list.Clone();