我有一个带有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
属性。有人可以帮我修改该方法,使其与返回任何类型的属性一起使用吗?
答案 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);
除非我们别无选择,否则任何“欺骗”编译器的东西都会使我们编译不应该编译的内容成为麻烦的秘诀。它带走了我们为防止运行时错误而必须使用的最强大的工具之一。
这与“ HttpContext
到HttpContextBase
,HttpRequest
到HttpRequestBase
等的适应方法基本相同,只是它们是抽象类而不是接口。
最初的实现没有实现接口或没有从抽象类继承,但是后来变得很明显,如果这些类有抽象(宽松地使用术语),那将是有帮助的。
因此,他们创建了具有与原始类完全相同的属性的新抽象类,就像您为某些现有类创建了新接口一样。
他们没有使用反射将现有的类映射到抽象类。他们只是使用了a wrapper,其行为类似于上面的代码。