生成所有属性都等效的复杂类型

Generating complex types with all properties equivalent

AutoFixture 是否可以使用相同的数据创建给定类型的多个实例?我的 类 不可序列化,我需要两个引用不等效但具有匹配属性的模型。

public class Foo
{
    // Many more properties and similar models needing the same semantics.
    public string Name { get; set; }
}

var a = fixture.Create<Foo>();
var b = fixture.Create<Foo>();

Assert.False(ReferenceEquals(a, b));
Assert.Equal(a.Name, b.Name);

我不认为 AutoFixture 可以做到这一点,但 Albedo 可以。虽然这只是概念验证代码,但我希望它应该能说明总体思路。

创建一个新的 class,派生自 ReflectionVisitor<T>:

public class PropertyCopyVisitor<T> : ReflectionVisitor<T>
{
    private readonly T source;
    private readonly T destination;

    public PropertyCopyVisitor(T source, T destination)
    {
        this.source = source;
        this.destination = destination;
    }

    public override IReflectionVisitor<T> Visit(
        PropertyInfoElement propertyInfoElement)
    {
        var pi = propertyInfoElement.PropertyInfo;
        pi.SetValue(this.destination, pi.GetValue(this.source));
        return this;
    }

    public override T Value
    {
        get { return this.destination; }
    }
}

实现的关键部分是 Visit 重载,它使用反射将每个 属性 从 source 复制到 destination 对象。

由于此实现改变了 destination,因此从未使用 Value 属性,但它必须存在,因为它在 ReflectionVisitor<T> 中是 abstract。 ..

您现在可以将测试编写为:

var fixture = new Fixture();
var a = fixture.Create<Foo>();
var b = fixture.Create<Foo>(); // Or just create a new, empty Foo...
// This copies all properties from a to b:
new TypeElement(typeof(Foo)).Accept(new PropertyCopyVisitor<Foo>(a, b));

Assert.False(ReferenceEquals(a, b));
Assert.Equal(a.Name, b.Name);

在这里,我仍然使用fixture创建b,但您不必这样做,因为无论如何所有属性都会被覆盖。如果 Foo 有一个无参数的构造函数,你可以简单地使用 new Foo();没什么区别。

此概念验证明确仅复制属性。如果您还需要复制字段,则还必须覆盖适当的 Visit 方法。此外,如果有问题的对象采用构造函数参数,您也需要显式处理这些参数。

如果你觉得写 new TypeElement(typeof(Foo)).Accept(new PropertyCopyVisitor<Foo>(a, b)); 很乏味,我相信你能找到一种方法来围绕它编写一个辅助方法。

作为附加说明,PropertyCopyVisitor 没有 是通用的,因为它实际上并没有将 T 类型参数用于任何东西.我只是喜欢这为构造函数提供的类型安全性...