强类型参数

时间:2015-10-21 17:14:17

标签: c#

出于性能原因,我不能使用Automapper或任何其他基于约定的映射工具。理想情况下,我想要一个我们可以调用的方法签名...例如 - mapper.Map<,>()但是以下不是有效的语法

   public MyModelView Map<Tin, Tout>(Tin source)
        where Tin: MyModelDto 
        where Tout: MyModelView 
    {
        return new MyModelView{
             Prop1 = source.Prop1;
        };
    }

    public MyModelDto Map<Tin, Tout>(Tin source)
        where Tin: MyModelView 
        where Tout: MyModelDto 
    {
        return new MyModelDto {
             Prop1 = source.Prop1;
        };
    }

一个有效但我个人觉得难看的解决方案如下。

    public void Map(MyModelDto source, out MyModelView outParam)
    {
        outParam = new MyModelView();
        outParam.Prop1 = source.Prop1;
    }

    public void Map(MyModelView source, out MyModelDto outParam)
    {
        outParam = new MyModelDto ();
        outParam.Prop1 = source.Prop1;
    }

    MyModelDto myDto = new MyModelDto { Prop1 = "Hello World" };
    MyModelView myView;
    mapper.Map(myDto, out myView);

我的问题是,有没有办法强制一个明确的类型而不使用反射或外部参数来强制唯一性,或者外部参数方法是我将得到的最干净的方法?

更新:所以我已经找到了满足我需求的解决方案,它使用的RuntimeTypeHandle并不是完全理想的,但它对我有用,并且使用起来最少。如果我的要求只允许每个对象进行一次类型转换,那么我会坚持使用更简单的解决方案。为了完整......这就是我想出的。

归功于类型处理理念:What is quicker, switch on string or elseif on type?

public class Mapper : IMapper
{
    private delegate object TypeHandler(Object node, Type desiredReturn);

    private static readonly Dictionary<RuntimeTypeHandle, TypeHandler> TypeLibrary = CreateTypeHandler();

    private static Dictionary<RuntimeTypeHandle, TypeHandler> CreateTypeHandler()
    {
        var ret = new Dictionary<RuntimeTypeHandle, TypeHandler>();

        ret[typeof(MyModelDto).TypeHandle] = HandleMyModelDto;
        //ret[typeof (Jill).TypeHandle] = HandleJill;
        //ret[typeof (Marko).TypeHandle] = HandleMarko;
        return ret;
    }

    private static object HandleMyModelDto(object source, Type desiredMapping)
    {
        MyModelDto sourceObj = source as MyModelDto;

        if (desiredMapping == typeof(MyModelView))
        {
            return new MyModelView { Prop1 = sourceObj.Prop1 };
        }
        else if (desiredMapping == typeof (MyModelBusiness))
        {
            return new MyModelBusiness { Prop1 = sourceObj.Prop1 };
        }
    }

    public Tout Map<Tin, Tout>(Tin source)
    {
        TypeHandler handler;
        if (TypeLibrary.TryGetValue(Type.GetTypeHandle(source), out handler))
        {
            return (Tout)handler(source, typeof(Tout));
        }
        else
        {
            //Unexpected type...
            throw new NotImplementedException("Type mapping not implemented");
        }
    }
}

消费如下

Mapper mapper = new Mapper();
MyModelDto myDto = new MyModelDto { Prop1 = "Hello World" };
MyModelView returnVal = mapper.Map<MyModelDto, MyModelView>(myDto);

2 个答案:

答案 0 :(得分:1)

不幸的是,在C#中,你不能定义两个具有相同签名的方法(即相同的方法名+参数)来返回两种不同的类型,但是,如果你的view和dto对象实现了一个公共接口,例如,某些东西如下:

public interface IMyModel
{
    string Prop1 { get; set; }
}

然后,您可以按如下方式定义Map方法:

public Tout Map<Tin, Tout>(Tin source)
    where Tin: IMyModel 
    where Tout: IMyModel, new() 
{
    var result = new Tout();
    result.Prop1 = source.Prop1;
    return result;
}

然后可以将其称为:

var view = Map<MyModelDto, MyModelView>(dtoInstance);
var dto = Map<MyModelView, MyModelDto>(viewInstance);

答案 1 :(得分:0)

如果您了解所涉及的类型和成员,为什么要尝试制作Map泛型?忘记TinTout,只使用您接受/返回的基本类型。

public MyModelView Map(MyModelDto source)
{
    return new MyModelView{
         Prop1 = source.Prop1
    };
}

基于MyModelDto的任何内容都必须包含Prop1,基于MyModelView的任何内容都必须包含Prop1。即使它是任何一个的子类,如果你要在没有反射的情况下使用它,那么这些成员必须在基类中定义。如果你有每个子类的变体,比如额外的成员,那么你需要为每个合法转换创建映射器函数的自定义版本(并且为此目的不使用类继承,实际上,因为它只增加了复杂性并且没有任何好处)。它将成为最快的代码,代价是维护映射例程。

  

仅当您只需将MyModelDto映射到另一个对象类型时,此方法才有效。假设我想将dto保存到我的db对象层或不同的视图模型。我将无法创建唯一的方法签名,因为C#不会将返回类型视为唯一方法签名的一部分

然后你可以:

public void Map(MyModelDto source, out MyModelView target)
{
    target = new MyModelView {
         Prop1 = source.Prop1
    };
}

但我宁愿建议您不要这样做,因为使用out参数可能很烦人。相反,使用MapToViewMapToModel等或保留Map但是通过不同的命名空间(对于扩展方法方法 - 可能是最佳选项)或静态类名称(执行{{1})来区分它们}和MapView.Convert()或者其他东西)。命名空间可能是最好的策略,因为在我看来,在大多数代码段中,你只会处理一个版本或另一个版本,而不是两者。