Skip to content

Instantly share code, notes, and snippets.

@andyedinborough
Created April 26, 2010 14:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andyedinborough/379389 to your computer and use it in GitHub Desktop.
Save andyedinborough/379389 to your computer and use it in GitHub Desktop.
Let you do a memberwise clone on a object of a base-type to an object of it's parent-type
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);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment