将接口应用于具有相同属性的类的通用方法

时间:2019-05-08 18:29:09

标签: c# moq system.reflection

我有一个带有string属性的接口:

public interface INameable
{
    string Name { get; }
    string Surname { get; }
}

和一个碰巧具有相同属性的类,但是没有实现接口。假设我无权修改该类(即该类是在外部DLL中实现的):

public class Person
{
    public string Name { get; set; }
    public string Surname { get; set; }
}

我已经编写了一个通用类,给定外部类的一个对象,它将使用该对象的所有值返回给我该接口的实例化:

public T ExtractInterface<T>(object argument) where T : class
{
    var mock = new Mock<T>();
    foreach (var property in typeof(T).GetProperties())
    {
        var returnValue = argument.GetType().GetProperties().SingleOrDefault(p => string.Equals(p.Name, property.Name, StringComparison.OrdinalIgnoreCase))?.GetValue(argument);

        ParameterExpression value = Expression.Parameter(typeof(T), "value");
        Expression setupProperty = Expression.Property(value, property.Name);
        var func = Expression.Lambda<Func<T, string>>(setupProperty, value);
        mock.Setup(func).Returns((string)returnValue);
    }
    return mock.Object;
}

可以这样使用:

var person = new Person { Name = "Joe", Surname = "Blogs" };
var personWithInterface = ExtractInterface<INameable>(person);
personWithInterface.Name.Dump();
personWithInterface.Surname.Dump();

此问题是,它仅适用于string属性。有人可以帮我修改该方法,使其与返回任何类型的属性一起使用吗?

1 个答案:

答案 0 :(得分:1)

这将更加容易,即使您必须一遍又一遍。与使用反射不同,如果类和接口不再巧合,则不会出现运行时错误。只是不会编译。

public class PersonWrapper : INameable
{
    private readonly Person _person;
    public PersonWrapper(Person person)
    {
        _person = person ?? throw new ArgumentNullException(nameof(person));
    }

    public string Name => _person.Name;
    public string Surname => _person.Surname;
}

如果您使用反射执行“泛型”操作,则会编译:

var list = new List<string>();
INameable person = ExtractInterface<INameable>(list);

除非我们别无选择,否则任何“欺骗”编译器的东西都会使我们编译不应该编译的内容成为麻烦的秘诀。它带走了我们为防止运行时错误而必须使用的最强大的工具之一。


这与“ HttpContextHttpContextBaseHttpRequestHttpRequestBase等的适应方法基本相同,只是它们是抽象类而不是接口。

最初的实现没有实现接口或没有从抽象类继承,但是后来变得很明显,如果这些类有抽象(宽松地使用术语),那将是有帮助的。

因此,他们创建了具有与原始类完全相同的属性的新抽象类,就像您为某些现有类创建了新接口一样。

他们没有使用反射将现有的类映射到抽象类。他们只是使用了a wrapper,其行为类似于上面的代码。