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