public
Last active

Let you do a memberwise clone on a object of a base-type to an object of it's parent-type

  • Download Gist
Memberwise-Clone-Example.cs
C#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
 
namespace ConsoleApplication1 {
public class a {
public string testa { get; set; }
public virtual string Who() {
return "I am class a!";
}
}
 
public class b : a {
public string testb { get; set; }
public override string Who() {
return "I am class b!";
}
}
 
public static class Program {
static void Main(string[] args) {
var b = new b() { testa = "a", testb = "b" };
 
//The version I use in my personal library is faster because it keeps a dictionary
//cache of the fields of each type, and creates get/set delegates
var a = b.CloneAs<a>();
 
Debug.Assert(b.Who() == "I am class b!");
Debug.Assert(((a)b).Who() == "I am class b!");
Debug.Assert(a.Who() == "I am class a!");
Debug.Assert(a.testa == "a");
}
 
public static T CloneAs<T>(this T input) where T : new() {
var output = new T();
output.CloneFrom(input);
return output;
}
 
/// <summary>
/// Updates one object with the fields of another.
/// </summary>
public static void CloneFrom<T>(this T ToObj, T FromObj) {
CloneFrom(ToObj, FromObj, new Dictionary<object, object>());
}
 
private static FieldInfo[] GetMemberFields(this Type type) {
return type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
}
 
private static void CloneFrom<T>(T ToObj, T FromObj, Dictionary<object, object> Values) {
Values.Add(FromObj, ToObj); //The dictionary is used to prevent infinite recursion
 
var fields = ToObj.GetType().GetMemberFields();
object value = null;
object othervalue = null;
foreach (var field in fields) {
value = field.GetValue(FromObj);
othervalue = field.GetValue(ToObj);
if (object.Equals(othervalue, value)) continue;
 
if (value != null && Values.ContainsKey(value)) {
//Prevent recursive calls: objA.fieldB.fieldC = objA
 
field.SetValue(ToObj, Values[value]);
} else if (othervalue != null && value != null && !field.FieldType.IsPrimitive && !field.FieldType.IsValueType && !(value is string)
&& field.FieldType.GetMemberFields().Any()) {
 
//Do not overwrite object references
CloneFrom(othervalue, value, Values);
} else {
field.SetValue(ToObj, value);
}
}
}
}
}

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.