基于反射的泛型模型转换

转载请注明出处:http://blog.csdn.net/water_0815/article/details/53240037

本文同步发表于我的微信公众号和简书社区。

微信公众号:惜福 / xifu_forever(扫一扫文章底部的二维码即可关注)

简书作者号:一度罪恶

这是我的第一篇技术性原创文章,平时分享的文章离代码都有点远,一时间不知道怎么写了,真是“万事开头难”,不管怎么样,先写了再说。就先分享点简单的东西吧,主要偏向于个人总结,如果对大家有所帮助,再好不过。

1.业务场景

也许大家都遇到过这种纠结,即如何从一个数据模型(姑且叫Model1)转换成另一个数据模型(Model2)?Model1和Model2有部分相同的属性、类型,我们希望将Model1属性的值赋于Model2相对应的属性,比如,将API接口数据模型转为数据库的数据模型。一般情况下,API接口接收到的数据包含了一些业务逻辑的字段,并不都需要保存到数据库。

当这种情况比较少时,我们可以在数据模型内部实现一个转换的方法,专门处理对应的API接口数据模型与数据库数据模型之间的转换映射。而这种需求比较多,Model又不确定的情况下,要是有一个能够处理并兼容各种Model之间的数据转换就好了。很高兴的是,一般的编程语言都支持泛型,今天我们就以C#为例,来实现这样一个方法。

2.技术实现

首先,创建两个测试的实体类(Model),UserInfoOrigin和UserInfoResult(此处也包含了泛型属性),如下所示:

public class UserInfoOrigin<T>
{
    public int UserId { set; get; }
    public string UserName { set; get; }
    public string Password { set; get; }
    public T UnknownType { set; get; }
    public EnumList.Sex Gender { set; get; }
}
public class UserInfoResult<T>
{
    public int UserId { set; get; }
    public string UserName { set; get; }
    //public string Password { set; get; }
    public T UnknownType { set; get; }
    public EnumList.Sex Gender { set; get; }
}

接着,再创建一个实体类TestModel,用来给UserInfoOrigin和UserInfoResult的泛型属性T指定类型。当然,你也可以指定其他基础数据结构的类型,比如Int,String,List,DateTime等,这里我们测试最复杂的Class类型。

public class TestModel
{
    public int TestId { set; get; }
    public string TestName { set; get; }
}

接下来,我们就要实现一个通用的处理方法,称为ConvertModel,要求传入一个Model1,返回另一个Model2,出来时的Model2就自动赋上Model1相对应的属性值了。如下所示:

/// <summary>
/// Model 转换
/// </summary>
/// <typeparam name="TOrigin">转换前Model泛型</typeparam>
/// <typeparam name="TResult">转换后Model泛型</typeparam>
/// <param name="modelOrigin">转换前Model</param>
/// <returns></returns>
public static TResult ConvertModel<TOrigin, TResult>(TOrigin modelOrigin)
{
    if (modelOrigin == null)
    {
        return default(TResult);
    }

    TResult modelRet = System.Activator.CreateInstance<TResult>();            
    PropertyInfo[] retModelPiInfos = typeof(TResult).GetProperties();
    PropertyInfo[] oriModelPiInfos = typeof(TOrigin).GetProperties();
    foreach (PropertyInfo piRet in retModelPiInfos)
    {
        String piName = piRet.Name;
        PropertyInfo piOri = oriModelPiInfos.Where(p => p.Name == piName).FirstOrDefault();
        if (piOri != null)
        {
            object valueOri = piOri.GetValue(modelOrigin);
            object valueRet = null;
            if (!string.IsNullOrEmpty(valueOri.ToString()))
            {
                valueRet = Convert.ChangeType(valueOri, piRet.PropertyType);
            }
            piRet.SetValue(modelRet, valueRet, null);
        }
    }

    return modelRet;
}

最后,我们来测试一下是不是得到了我们想要的结果,测试代码如下所示:

static void Main(string[] args)
{
    TestModel tm = new TestModel { 
        TestId = 0,
        TestName = "TestNameValue"
    };

    UserInfoOrigin<TestModel> uOrigin = new UserInfoOrigin<TestModel>
    {
        UserId = 1,
        UserName = "李海波",
        Password = "abc123",
        UnknownType = tm,
        Gender = EnumList.Sex.男
    };

    UserInfoResult<TestModel> uResult = ModelTool.ConvertModel<UserInfoOrigin<TestModel>, UserInfoResult<TestModel>>(uOrigin);
    if (uResult != null)
    {
        Console.WriteLine(uResult.ToString());
    }
    else
    {
        Console.WriteLine("null");
    }

    Console.ReadKey();           
}

调试运行时,我们对UserInfoResult类型的uResult进行监视,得到以下结果,看来是对了:
测试结果

今天的分享就到这。

关注我的微信公众号,专注于分享读书心得、编程艺术、随笔臆想等。微信扫一扫下方二维码即可关注:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值