不同类型不同字段如何实现深拷贝(可配置)
突发奇想,如果给我两个实体 A 与 B 他们的字段是这样的,实现将A对象的值赋值给B对象
public class A
{
public string Name { set; get; }
public int Age { set; get; }
public DateTime BirthDate { set; get; }
public string Email { set; get; }
}
public class B
{
public string PersonName { set; get; }
public int PersonAge { set; get; }
public DateTime PersonBirthDate { set; get; }
public string PersonEmail { set; get; }
}
最简单的通过 new 一个B 对象 将A里的值一个字段一个字段赋值给B,这种方式可以实现,但是写死的东西不灵活且如果字段超多怎么办?
如何才能将A类的Name字段赋给B类的PersonName字段同样达到可配置字段赋给任意字段的需求呢
通过最常用的反射去实现似乎有些麻烦,可以通过表达式树的方式实现:
public static class DeepCopy
{
private static ConcurrentDictionary<string, object> actions = new ConcurrentDictionary<string, object>();
/// <summary>
/// 深拷贝
/// </summary>
/// <typeparam name="S">源类型</typeparam>
/// <typeparam name="T">目标类型</typeparam>
/// <param name="source">原对象</param>
/// <param name="target">目标对象</param>
/// <param name="dics">配置</param>
public static void Copy<S, T>(S source, T target, Dictionary<string, string> dics = null)
{
string typeName = string.Format("{0}_{1}", typeof(S), typeof(T));
if (!actions.TryGetValue(typeName, out object obj))
{
Action<S, T> executeAct = CreateCopier<S, T>(dics);
actions.TryAdd(typeName, executeAct);
obj = executeAct;
}
Action<S, T> action = (Action<S, T>)obj;
action(source, target);
}
private static Action<S, T> CreateCopier<S, T>(Dictionary<string,string> dics)
{
var source = Expression.Parameter(typeof(S));
var target = Expression.Parameter(typeof(T));
BlockExpression block = null;
if (dics == null)//如果未传入配置则按相同字段名进行赋值
{
var sourceParameter = typeof(S).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanRead).ToList();
var targetParameter = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanWrite).ToList();
//根据相同的字段名
var intersection = sourceParameter.Where(x => targetParameter.Where(y => y.Name == x.Name).Count() > 0);
block = Expression.Block(intersection.Select(property => Expression.Assign(Expression.Property(target, property.Name), Expression.Property(source, property.Name))));
}
else
{
//根据配置来进行赋值 Key为B类中的字段 Value为A类中的字段
block = Expression.Block(dics.Select(dic => Expression.Assign(Expression.Property(target, dic.Key), Expression.Property(source, dic.Value))));
}
return Expression.Lambda<Action<S, T>>(block, source, target).Compile();
}
}
例1 无配置传入根据相同字段进行赋值:
A a = new A
{
Name = "小张",
Age = 25,
BirthDate = DateTime.Now,
Email = "123@123.com"
};
//因为没有传入配置文件此处只赋值了相同字段 固只有Name具有值
B b1 = new B();
DeepCopy.Copy<A, B>(a,b1);
Console.WriteLine(b1.Name);
例2 有配置传入根据配置字段进行赋值:
A a = new A
{
Name = "小张",
Age = 25,
BirthDate = DateTime.Now,
Email = "123@123.com"
};
B b2 = new B();
Dictionary<string, string> dic = new Dictionary<string, string>
{
{ "Name", "Name" },
{ "PersonAge", "Age" },
{ "PersonBirthDate", "BirthDate" },
{ "PersonEmail", "Email" }
};
DeepCopy.Copy<A, B>(a, b2,dic);
Console.WriteLine("姓名:"+b2.Name);
Console.WriteLine("年龄:" + b2.PersonAge);
Console.WriteLine("出生日期:" + b2.PersonBirthDate);
Console.WriteLine("邮箱:" + b2.PersonEmail);
这里再说一下
block = Expression.Block(dics.Select(dic => Expression.Assign(Expression.Property(target, dic.Key), Expression.Property(source, dic.Value))));
配置文件不光可以是键值对类型,只要是可以使用linq遍历并且可以取出值的都可以,最简单的例如配置文件存在数据库中取出来后通过select取出需要的值等等
本文主要研究在Copy对象的时候遇到字段不相同还需拷贝的情况
记录一下工作遇到的小问题。如有问题,还请大牛不吝赐教。
版权声明:本文为博主原创文章,未经博主允许不得转载。