我正在尝试清理我的许多代码,这些代码与创建Web服务请求以及在视图模型和数据传输对象之间来回映射有关。 我目前在我的代码中有以下设置:
public class Request1 : IRequest<Type1>
{
public Type1 Data { get; set; }
}
public interface IRequest<T>
{
T Data { get; set; }
}
public class Type1
{
string Account {get; set; }
...
}
public static class Mapper
{
public static TOut CreateRequest<TOut, TIn>(TIn data) where TIn : IRequest<TIn>, new()
{
var request = new TOut();
request.Data = data;
return request;
}
}
上面的内容使我能够通过简单地传入数据来使用泛型来创建Request1
对象。
var model = new ViewModel();
var data = Mapper.mapFromViewModel(model);
var request = Mapper.CreateRequest<Request1,Type1>(data);
我认为这是 GREAT ,但它有一个缺陷
我传入的数据必须是请求中的dto类型(Type1
)。我想要做的是以原始形式传递数据,因此在UI中我将传递视图模型数据和我想要的请求类型。该方法将计算出视图模型和dto类型之间的映射,然后为我创建请求。
因此,如果我使用一点AutoMapper
来处理视图模型和dto类型之间的映射,我可以这样做:
public TOut CreateRequest2<TOut, TDto, TModelIn>(TModelIn data)
where TOut : IRequest<TDto>, new()
{
var request = new TOut();
request.Data = Map<TModelIn, TDto>(data);
return request;
}
我可以这样使用:
var model = new ViewModel();
var request = Mapper.CreateRequest2<Request1,Type1,ViewModel>(model);
几乎那里......但这就是我掌握知识的地方
我希望能够从通话中删除TDto
要求,因此来电者只需了解Request
和ViewModel
类型。
类似的东西:
public TOut CreateRequest3<TOut, TModelIn>(TModelIn data)
{
// Cast? the TOut to IRequest<>
// Extract the TDto type from the IRequest<> object and
// pass the TDto type into the method I created earlier.
return CreateRequest2<TOut,TDto,TModelIn>(data);
}
有谁知道这是否可以实现?
这样做的好处是不需要我的UI代码知道消息需要什么DTO,并且让它由自动播放器根据它给出的视图模型来处理。
更新 因此,根据@EpicSam的建议,我现在有以下工作:
public TOut CreateRequest3<TOut, TModelIn>(TModelIn data) where TOut : class, new()
{
var interfaces = typeof(TOut).GetInterfaces().FirstOrDefault(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IRequest<>));
var dtoType = interfaces?.GenericTypeArguments.FirstOrDefault();
var result = typeof(Mapper)
.GetMethod(nameof(Mapper.CreateRequest))
.MakeGenericMethod(typeof(TOut), dtoType, typeof(TModelIn))
.Invoke(this, new object[ ] {data});
return result as TOut;
}
这很丑陋,但没关系,因为它只写了一次。但是......我需要知道关于这段代码的其他事情。因为它使用反射,是否会影响我的表现,或者我甚至不应该担心它......
更新2: 反射代码比我发布的原始代码慢约3倍。因此,即使我可以选择使我的代码更好更清洁,我也会选择保持原样,并且工作效率更高。
答案 0 :(得分:2)
泛型用于编译时,而不是运行时。如果您在运行时需要它,您必须使用反射并执行以下操作:
public TOut CreateRequest3<TOut, TModelIn>(TModelIn data) {
var type = typeof(TOut).GetProperty("Data").PropertyType;
return typeof(Mapper).GetMethod("CreateRequest2").MakeGenericMethod(new Type[] {TOut, type, TModelIn} ).Invoke(this, new Object[] {data});
}
这很难看,或许你想要实现的目标有不同的方法吗?
答案 1 :(得分:1)
你可以这样做,滥用扩展方法:
public interface IRequest
{
}
public interface IRequest<T> : IRequest
{
T Data { get; set;}
}
public static class Mapper
{
public static TOut CreateRequest<TOut>() where TOut : IRequest, new()
{
return new TOut();
}
public static IRequest<TDto> From<TDto,TModel>(this IRequest<TDto> request, TModel data)
{
request.Data=Map<TModel,TDto>(data);
return request;
}
public static TOut Map<TIn,TOut>(TIn input)
{
// Only for this example, you need to provide your own implemenation.
return (TOut)(object)((Model)(object)input).Value;
}
}
然后你可以像这样调用它:
Mapper.CreateRequest<SomeRequestType>().From(myModel);
请参阅here