简体   繁体   中英

Deep copy via reflection involving parameterized constructors

I am trying to create a class hierarchy where a BaseClass.GetCopy() method would provide copies of the same runtime subclass, with the same readonly ID property (passed via constructor), and with every public writeable property copied.

I got the code below, but the tests don't pass, since I didn't know how to implement reflection-based property copying . Also, the constructors in base class became duplicated (error-prone, IMO), and the parameterized one is public, which is not a good thing in this case, since I don't want the client code to be able to set Ids explicitly.

My questions are stated as comments in the code below:

[TestFixture]
public class RepoItemTests
{
    [Test]
    public void CloneHasSameId()
    {
        var one = new ConcreteRepoItemName();
        var two = one.GetCopy();

        Assert.AreEqual(one.Id, two.Id);
    }

    [Test]
    public void CloneIsSubclassInstance()
    {
        var one = new ConcreteRepoItemAge();
        var two = one.GetCopy();

        Assert.IsInstanceOf<ConcreteRepoItemAge>(two);
    }

    [Test]
    public void ChangingCloneNameDoesntChangeOriginalAge()
    {
        var one = new ConcreteRepoItemName() { Name = "original" };
        var two = one.GetCopy() as ConcreteRepoItemName;
        Assert.AreEqual(one.Name, two.Name);

        two.Name = "modified";
        Assert.AreNotEqual(one.Name, two.Name);
    }

    [Test]
    public void ChangingCloneAgeDoesntChangeOriginalAge()
    {
        var one = new ConcreteRepoItemAge() { Age = 22 };
        var two = one.GetCopy() as ConcreteRepoItemAge;
        Assert.AreEqual(one.Age, two.Age);

        two.Age = 33;
        Assert.AreNotEqual(one.Age, two.Age);
    }
}


public class ConcreteRepoItemName : AbstractRepoItem<ConcreteRepoItemName>
{
    public ConcreteRepoItemName() : base() { }

    // I don't want the constructor below to be public
    public ConcreteRepoItemName(Guid id) : base(id) { }


    public string Name { get; set; }
}


public class ConcreteRepoItemAge : AbstractRepoItem<ConcreteRepoItemAge>
{
    public ConcreteRepoItemAge() : base() { }

    // I don't want the constructor below to be public
    public ConcreteRepoItemAge(Guid id) : base(id) { }


    public decimal Age { get; set; }
}


public abstract class AbstractRepoItem<T> where T : AbstractRepoItem<T>, new()
{
    public AbstractRepoItem()
    {
        Id = Guid.NewGuid();
    }

    // I don't want the constructor below to be public
    protected AbstractRepoItem(Guid id)
    {
        Id = id;
    }

    public Guid Id { get; private set; }

    public T GetCopy()
    {
        var clone = Activator.CreateInstance(typeof(T), new object[] { Id }) as T;

        /// HOW DO I COPY RUNTIME PROPERTIES HERE VIA REFLECTION?

        return clone;
    }
}

I have created a method that can serve your propose. The idea is to iterate throw all the fields in the object and create a copy of it that way you can make your evaluations for testing.

public static T Copy<T>(T obj)
{
    if (obj == null) throw new ArgumentNullException("obj");
    Type Typeobj = obj.GetType();
    var ResultObj = Activator.CreateInstance(Typeobj);
    Type ResultObjType = ResultObj.GetType();

    foreach (var field in Typeobj.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance))
    {
        FieldInfo f = ResultObjType.GetField(field.Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

        f.SetValue(ResultObj, field.GetValue(obj));

    }

    return (T) ResultObj;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM