一、概述
本文主要研究如何通过预编译表达式precomplied expression)在已创建的同类型对象间快速进行浅拷贝的问题。要理解本文的内容,需要对C#语言和.net平台的表达式相关类库有基本的了解。
二、背景
关于C#中的对象拷贝问题,网上已有不少文章说过在C#中使用多种方法将已有对象的快速克隆出一个新创建对象。象这篇转载文章(https://www.cnblogs.com/lsgsanxiao/p/8205096.html)(附带一说,此文章中的所有方法仍是浅拷贝,并不是深拷贝)对现有了几种方法进行了对比,再加上.net平台自带的Object.MemberwiseClone()函数,可以说在对象浅拷贝克隆方面比较全面了。但一个问题是目前的方法均针对从已有对象通过浅拷贝克隆出新对象,基本没有看到如何在两个同类型的已有对象间快速进行浅拷贝。这篇文章在前述文章的基础上,对通过预编译表达式克隆出新对象的方法加以改进,给出了一种通过预编译表达式进行已有对象间快速浅拷贝的方法。
三、算法
基本的算法类同文章(https://www.cnblogs.com/lsgsanxiao/p/8205096.html)的3.4节,使用泛型优化的预编译表达式来处理。区别在于我们的输入函数接受两个参数,将第一个参数的成员值拷贝给第二个参数的同名成员,不创建新的对象。下面先看代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Numerics;
using System.Threading.Tasks;
namespace TestCloneTo
{
class Program
{
static void Main(string[] args)
{
A a = new A() { PInt = 1, PString = "a" };
A b = new A() { PInt = 2, PString = "b" };
Console.WriteLine($"Before Clone: a: {a.ToString()}!");
Console.WriteLine($"Before Clone: b: {b.ToString()}!");
CloneToExp<A, A>.CloneTo(a, b);
Console.WriteLine($"After Clone: a: {a.ToString()}!");
Console.WriteLine($"After Clone: b: {b.ToString()}!");
Console.ReadLine();
}
}
public class A
{
public int PInt { get; set; }
public string PString { get; set; }
public string ToString()
{
return $"{{PInt={PInt}, PString={PString}}}";
}
}
public static class CloneToExp<TIn, TOut>
{
private static Func<TIn, TOut, TOut> cache = GetFunc();
private static Func<TIn, TOut, TOut> GetFunc()
{
ParameterExpression parameterInExpression = Expression.Parameter(typeof(TIn), "pIn");
ParameterExpression parameterOutExpression = Expression.Parameter(typeof(TOut), "pOut");
List<Expression> memberBindingList = new List<Expression>();
foreach (var item in typeof(TOut).GetProperties())
{
if (!item.CanWrite)
continue;
MemberExpression propertyIn = Expression.Property(parameterInExpression, typeof(TIn).GetProperty(item.Name));
MemberExpression propertyOut = Expression.Property(parameterOutExpression, item);
BinaryExpression memberAssign = Expression.Assign(propertyOut, propertyIn);
memberBindingList.Add(memberAssign);
}
memberBindingList.Add(parameterOutExpression); //此处需返回pOut以与表达式定方的Func<TIn,TOut,TOut>匹配
BlockExpression memberAssignExpression = Expression.Block(memberBindingList.ToArray());
Expression<Func<TIn, TOut, TOut>> lambda = Expression.Lambda<Func<TIn, TOut, TOut>>(memberAssignExpression, new ParameterExpression[] { parameterInExpression, parameterOutExpression });
return lambda.Compile();
}
public static TOut CloneTo(TIn tIn, TOut tOut)
{
return cache(tIn, tOut);
}
}
}
简单解释一下,代码主要是创建了CloneToExp<TIn,TOut>这个泛型类,这个类中通过GetFunc()静态函数动态创建了一个Lambda表达式。这个表达式类同于:
(pIn, pOut) => {
pOut.Prop1 = pIn.Prop1;
pOut.Prop2 = pIn.Prop2;
.
.
.
pOut.PropN = pIn.PropN;
pOut; //此处需返回pOut以与表达式定方的Func<TIn,TOut,TOut>匹配
}
表达式的相关意义与相关函数可查询MSDN,此处不再赘述。
四、总结
本文介绍了如何通过预编译表达式实现已有对象间的快速浅拷贝,是对现有对象快速克隆方法的一个补充。一个需要继续研究的问题是能否通过表达式进行对象间的深拷贝。对象的深拷贝一般涉及到浅拷贝的递归调用,即对象的属性也是一个对象,需对对象类型的属性再调用浅拷贝算法,目前尚不清楚表达式能否进行递归调用,不能的话,还是只能用反射的方法来解决,待后续研究。