0

How to do a deep copy of objects using System.Reflection in C# ?

2
  • 1
    why do you need Reflection ? and what you have tried so far Commented Nov 13, 2015 at 15:52
  • Recursion over properties/fields. Question is which one to include, what to do with private fields, etc. Therefore Clone is way easier to do with specially prepared for serialization object by serializing existing and deserializing a new one. Commented Nov 13, 2015 at 15:53

1 Answer 1

4

One simple way is to use JSON:

public static T DeepClone<T>(T source)
{
    var serialized = JsonConvert.SerializeObject(source);
    return JsonConvert.DeserializeObject<T>(serialized);
}

which does the reflection for you. Obviously it won't work with anything that, for example, has a handle to an unmanaged object and so on.

(You can use NuGet to install Newtonsoft.Json into your project.)

By default Json won't serialise private fields.

You can fix that like so:

public static T DeepClone<T>(T source)
{
    var settings = new JsonSerializerSettings {ContractResolver = new MyContractResolver()};
    var serialized = JsonConvert.SerializeObject(source, settings);
    return JsonConvert.DeserializeObject<T>(serialized);
}

public class MyContractResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                        .Select(p => base.CreateProperty(p, memberSerialization))
                    .Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                                .Select(f => base.CreateProperty(f, memberSerialization)))
                    .ToList();
        props.ForEach(p => { p.Writable = true; p.Readable = true; });
        return props;
    }
}

Here's a full sample console app that shows how an arbitrary class with private fields can be cloned. Note that Json tries to use a constructor to set the fields and/or properties, and if the constructor parameter names don't match the field or property names it won't work correctly:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

namespace ConsoleApplication1
{
    class Test
    {
        public Test(double y, string s, int x)
        {
            this.Y = y;
            this.s = s;
            this.X = x;
        }

        public int X;

        public double Y { get; private set; }

        public string Z         
        {
            get
            {
                return s;
            }
        }

        private string s;
    }

    class Program
    {
        static void Main()
        {
            var test = new Test(1.2345, "12345", 12345);
            test.X = 12345;

            var copy = DeepClone(test);

            Console.WriteLine("X = " + copy.X);
            Console.WriteLine("Y = " + copy.Y);
            Console.WriteLine("Z = " + copy.Z);
        }

        public static T DeepClone<T>(T source)
        {
            var settings = new JsonSerializerSettings {ContractResolver = new MyContractResolver()};
            var serialized = JsonConvert.SerializeObject(source, settings);
            return JsonConvert.DeserializeObject<T>(serialized);
        }

        public class MyContractResolver : DefaultContractResolver
        {
            protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
            {
                var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                                .Select(p => base.CreateProperty(p, memberSerialization))
                            .Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                                        .Select(f => base.CreateProperty(f, memberSerialization)))
                            .ToList();
                props.ForEach(p => { p.Writable = true; p.Readable = true; });
                return props;
            }
        }
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

This will not work for just any object. Object has to be ready for serialization.
@Sinatr It will work for many objects - if you need to use recursion then the object will have to be serializable anyway.
What I mean is you have to either create special public properties to carry object state or mark certain private fields (as example) with attributes. Then Json is not bad, but I am not sure if this choice is the best (fastest?).
Bravo @MatthewWatson it is working. just be sure that copy.Equals(test) =false. and that's the case

Your Answer

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