从方法

时间:2016-11-14 13:10:25

标签: c# generics

我正在尝试清理我的许多代码,这些代码与创建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要求,因此来电者只需了解RequestViewModel类型。

类似的东西:

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倍。因此,即使我可以选择使我的代码更好更清洁,我也会选择保持原样,并且工作效率更高。

2 个答案:

答案 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