How to do a deep copy of objects using System.Reflection in C# ?
Asked
Active
Viewed 1,893 times
0
-
1why do you need `Reflection` ? and what you have tried so far – Habib Nov 13 '15 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. – Sinatr Nov 13 '15 at 15:53
1 Answers
3
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;
}
}
}
}

Matthew Watson
- 104,400
- 10
- 158
- 276
-
1This will not work for just any object. Object has to be ready for serialization. – Sinatr Nov 13 '15 at 15:56
-
1@Sinatr It will work for many objects - if you need to use recursion then the object will have to be serializable anyway. – Matthew Watson Nov 13 '15 at 15:58
-
1What 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](http://stackoverflow.com/q/4906368/1997232)?). – Sinatr Nov 13 '15 at 16:00
-
Bravo @MatthewWatson it is working. just be sure that copy.Equals(test) =false. and that's the case – Mar 07 '16 at 11:53