public static class ObjectCloner { public static object Clone(this object root) { Type rootType = root.GetType(); object clone = Activator.CreateInstance(rootType); foreach (PropertyInfo pi in rootType.GetProperties()) { bool isCollection = pi.PropertyType.IsGenericType && typeof(IEnumerable).IsAssignableFrom(pi.PropertyType); if (!isCollection) { object transientVal = rootType.InvokeMember(pi.Name, BindingFlags.GetProperty, null, root, new object[] { }); rootType.InvokeMember(pi.Name, BindingFlags.SetProperty, null, clone, new object[] { transientVal }); } else { var colOrig = (IList)rootType.InvokeMember(pi.Name, BindingFlags.GetProperty, null, root, new object[] { }); object clonedList = Activator.CreateInstance(colOrig.GetType()); rootType.InvokeMember(pi.Name, BindingFlags.SetProperty, null, clone, new object[] { clonedList }); CloneCollection(root, (IList)colOrig, clone, (IList)clonedList); } } return clone; } private static void CloneCollection(object origParent, IList origList, object cloneParent, IList cloneList) { foreach (object item in origList) { object cloneItem = item.Clone(); foreach(PropertyInfo pi in cloneItem.GetType().GetProperties().Where(x => x.PropertyType == origParent.GetType())) { object val = cloneItem.GetType().InvokeMember(pi.Name, BindingFlags.GetProperty, null, cloneItem, new object[] { }); if (object.ReferenceEquals(val,origParent)) { // point it to its new parent cloneItem.GetType().InvokeMember(pi.Name, BindingFlags.SetProperty, null, cloneItem, new object[] { cloneParent }); } } cloneList.Add(cloneItem); } } }
To use:
[TestMethod] public void Can_do_deep_clone() { Question orig = PopulateQuestion(); Question clone = (Question)orig.Clone(); Assert.AreNotSame(orig, clone); Assert.AreNotSame(orig.Answers, clone.Answers); Assert.AreSame(orig, orig.Answers[0].Question); Assert.AreSame(clone, clone.Answers[0].Question); Assert.AreNotSame(orig.Answers[0], clone.Answers[0]); Assert.AreNotSame(orig.Answers[0].Question, clone.Answers[0].Question); Assert.AreNotSame(orig.Answers[1].Question, clone.Answers[1].Question); Assert.AreNotSame(orig.Answers[1].Comments, clone.Answers[1].Comments); Assert.AreNotSame(orig.Answers[1].Comments[0], clone.Answers[1].Comments[0]); Assert.AreSame(orig.Answers[1], orig.Answers[1].Comments[0].Answer); Assert.AreSame(clone.Answers[1], clone.Answers[1].Comments[0].Answer); Assert.AreEqual(orig.Text, clone.Text); Assert.AreEqual(orig.Answers.Count, clone.Answers.Count); Assert.AreEqual(orig.Answers[0].Text, clone.Answers[0].Text); Assert.AreEqual(orig.Answers[1].Comments[0].Text, clone.Answers[1].Comments[0].Text); } private static Question PopulateQuestion() { var importantQuestion = new Question { Text = "The answer to life", Poster = "Boy", Answers = new List<Answer>(), Comments = new List<QuestionComment>() }; var answerA = new Answer { Question = importantQuestion, Text = "42", Poster = "John", Comments = new List<AnswerComment>() }; var answerB = new Answer { Question = importantQuestion, Text = "143", Poster = "Paul", Comments = new List<AnswerComment>() }; var answerC = new Answer { Question = importantQuestion, Text = "888", Poster = "Elton", Comments = new List<AnswerComment>() }; importantQuestion.Answers.Add(answerA); importantQuestion.Answers.Add(answerB); importantQuestion.Answers.Add(answerC); var commentToImportantQuestion = new QuestionComment { Question = importantQuestion, Text = "Is There?", Poster = "George" }; importantQuestion.Comments.Add(commentToImportantQuestion); var commentToAnswerB = new AnswerComment { Answer = answerB, Text = "Isn't the answer is 7 times 6?", Poster = "Ringo" }; answerB.Comments.Add(commentToAnswerB); return importantQuestion; }
Object structure sample:
public class Question { public virtual int QuestionId { get; set; } public virtual string Text { get; set; } public virtual string Poster { get; set; } public virtual IList<QuestionComment> Comments { get; set; } public virtual IList<Answer> Answers{ get; set; } public virtual byte[] RowVersion { get; set; } } public class QuestionComment { public virtual Question Question { get; set; } public virtual int QuestionCommentId { get; set; } public virtual string Text { get; set; } public virtual string Poster { get; set; } } public class Answer { public virtual Question Question { get; set; } public virtual int AnswerId { get; set; } public virtual string Text { get; set; } public virtual string Poster { get; set; } public virtual IList<AnswerComment> Comments { get; set; } } public class AnswerComment { public virtual Answer Answer { get; set; } public virtual int AnswerCommentId { get; set; } public virtual string Text { get; set; } public virtual string Poster { get; set; } }
No comments:
Post a Comment