Generating complex types with all properties equivalent

丶灬走出姿态 提交于 2019-12-13 17:41:14

问题


Does AutoFixture have a facility to create multiple instances of a given type with all the same data? My classes are not serializable and I need two models which are not reference equivalent, but instead have matching properties.

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

回答1:


I don't think that AutoFixture can do this, but Albedo can. While this is only proof-of-concept code, it should illustrate the general idea, I hope.

Create a new class, deriving from 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; }
    }
}

The pivotal part of the implementation is the Visit overload where it uses Reflection to copy each property from source to destination object.

Since this implementation mutates destination, the Value property is never used, but it has to be there because it's abstract in ReflectionVisitor<T>...

You can now write your test as:

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

Here, I still used fixture to create b, but you don't have to do that, because all properties are going to be overwritten anyway. If Foo has a parameterless constructor, you could instead simply use new Foo(); it'd make no difference.

This proof-of-concept explicitly only copies properties. If you need to copy fields as well, you'll have to override the appropriate Visit method as well. Furthermore, if the objects in question take constructor arguments, you'll need to deal with those explicitly as well.

If you find it tedious to write new TypeElement(typeof(Foo)).Accept(new PropertyCopyVisitor<Foo>(a, b));, I'm sure you can figure out a way to write a helper method around it.

As an additional note, PropertyCopyVisitor doesn't have to be generic, because it doesn't actually use the T type argument for anything. I just like the type safety that this gives the constructor...



来源:https://stackoverflow.com/questions/47165015/generating-complex-types-with-all-properties-equivalent

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!