通过反射涉及参数化构造函数的深层复制

时间:2016-10-04 14:23:59

标签: c# reflection deep-copy

我正在尝试创建一个类层次结构,其中BaseClass.GetCopy()方法将提供相同运行时子类的副本,具有相同的readonly ID属性(通过构造函数传递),并且每个公共可写属性都被复制。

我得到了下面的代码,但测试没有通过,因为我不知道 如何实现基于反射的属性复制 。此外,基类中的构造函数变得重复(容易出错,IMO),参数化的构造函数是公共的,在这种情况下这不是一件好事,因为我不希望客户端代码能够设置明确地说。

我的问题在下面的代码中作为评论说明:

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

1 个答案:

答案 0 :(得分:0)

我已经创建了一个可以满足您建议的方法。 我们的想法是迭代抛出对象中的所有字段并创建它的副本,这样您就可以进行测试评估。

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