mardi 4 octobre 2016

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;
    }
}





Aucun commentaire:

Enregistrer un commentaire