C# 通过反射实现对象映射:将2个属性相近的对象相互转换

时间:2024-02-17 10:27:28

前言

我们在编程过程中,经常需要将一个对象转成另一个对象(一般称为对象映射)。
比如我们有2个类:

//第1个类 CLS1
class CLS1
{
    public int i {get; set;}
	public string str {get; set;}
}
//第2个类 CLS2
class CLS2
{
    public int i {get; set;}
	public string str {get; set;}
}

两个类都拥有属性 istr
当我们需要将 CLS1 的实例对象转化为 CLS2 的实例对象时,正常会这样操作:

CLS1 obj1 = new CLS1(){i = 1, str = "ss"};
CLS2 obj2 = new CLS2();
//将 obj1 映射为 obj2
obj2.i = obj1.i; //obj2.i = 1; 
obj2.str = obj1.str; //obj2.str = "ss";

如果属性多了,写起来会很繁琐,希望可以通过一个方法自动帮我们解决,比如这样:

//将 CLS1 的对象映射为 CLS2 的对象
obj2 = Mapper.T1MapToT2<CLS1, CLS2>(obj1); //obj2.i = 1; obj2.str = "ss";

实现

以下是实现代码:

public class Mapper
{
    /// <summary>
    /// 通过反射,将 T1 映射为 T2
    /// </summary>
    /// <typeparam name="T1"></typeparam>
    /// <typeparam name="T2"></typeparam>
    /// <param name="t1"></param>
    /// <returns></returns>
    public static T2 T1MapToT2<T1, T2>(T1 t1)
        where T1 : class
        where T2 : class //, new()
    {
        T2 t2 = Activator.CreateInstance<T2>();  //T2 t2 = new T2(); //后面这种写法,要在 where 中添加 new()
        if (t1 == null)
        {
            return t2;
        }

        var p1 = t1.GetType().GetProperties();
        var p2 = typeof(T2).GetProperties();
        for (int i = 0; i < p1.Length; i++)
        {
            //条件:1、属性名相同;2、t2属性可写;3、属性可读性一致;4、数据类型相近(相同,或者接近。接近如:int 和 int?)
            var p = p2.Where(t => t.Name == p1[i].Name && t.CanWrite && t.CanRead == p1[i].CanRead).FirstOrDefault();
            if (p == null)
                continue;
            var v = p1[i].GetValue(t1);
            if (v == null)
                continue;
            try { p.SetValue(t2, v); } //难判定数据类型,暂时这样处理
            catch
            {
                try { p.SetValue(t2, Convert.ChangeType(v, p.PropertyType)); } //int? -> object -> int? 会抛错
                catch { }
            }

        }

        return t2;
    }

    //这种写法和上面的写法没啥差别
    public static T2 T1MapToT2_2<T1, T2>(T1 t1)
        where T1 : class
        where T2 : class //, new()
    {
        T2 t2 = Activator.CreateInstance<T2>();  //T2 t2 = new T2(); //后面这种写法,要在 where 中添加 new()

        var p1 = t1.GetType().GetProperties();
        var p2 = typeof(T2);
        for (int i = 0; i < p1.Length; i++)
        {
            //条件:1、属性名相同;2、t2属性可写;3、属性可读性一致;4、数据类型相近(相同,或者接近。接近如:int 和 int?)
            var p = p2.GetProperty(p1[i].Name);
            if (p == null || !p.CanWrite || p.CanRead != p1[i].CanRead)
                continue;
            var v = p1[i].GetValue(t1);
            if (v == null)
                continue;
            try { p.SetValue(t2, Convert.ChangeType(v, p.PropertyType)); }
            catch { }
        }

        return t2;
    }
}