对于实现任意对象的Clone,以前也看到过不少讨论 的文章和实现。暂时先发到首页,供大家讨论,如果觉得不合适我会拆下
以下是Whizzo's 的两篇文章:
Object Cloning Using IL in C#
这篇文章作者分别用IL,反射实现了浅拷贝,并对普通的clone、IL、反射这三种实现方式做性能的对比
Object Deep Cloning using IL in C# - version 1.0
这篇文章作者分别用IL,反射实现了浅拷贝和深拷贝,并对普通的clone、IL深拷贝、IL浅拷贝、反射这三种实现方式做性能的对比,
目前只支持Colone的对象具有默认构造函数的对象。
Code
using System;
using System.Collections.Generic;
using System.Text;
namespace Cloning
{
public class Person
{
private int _id;
private string _name;
private string _firstName;
private string _field1, _field2, _field3;
public Person()
{
this.Addresses = new List<Address>();
}
public int ID
{
get { return _id; }
set { _id = value; }
}
public string Name
{
get { return _name; }
set { _name = value; }
}
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
private Address _address;
public Address Address
{
get { return _address; }
set { _address = value; }
}
private List<Address> _addresses;
public List<Address> Addresses
{
get { return _addresses; }
set { _addresses = value; }
}
}
public class Address
{
public Address()
{
this.AddressID = -1;
}
public Address(int aid)
{
this.AddressID = aid;
}
private int _addressID;
public int AddressID
{
get { return _addressID; }
set { _addressID = value; }
}
private string _street;
public string Street
{
get { return _street; }
set { _street = value; }
}
private string _city;
public string City
{
get { return _city; }
set { _city = value; }
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Cloning
{
public class Person
{
private int _id;
private string _name;
private string _firstName;
private string _field1, _field2, _field3;
public Person()
{
this.Addresses = new List<Address>();
}
public int ID
{
get { return _id; }
set { _id = value; }
}
public string Name
{
get { return _name; }
set { _name = value; }
}
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
private Address _address;
public Address Address
{
get { return _address; }
set { _address = value; }
}
private List<Address> _addresses;
public List<Address> Addresses
{
get { return _addresses; }
set { _addresses = value; }
}
}
public class Address
{
public Address()
{
this.AddressID = -1;
}
public Address(int aid)
{
this.AddressID = aid;
}
private int _addressID;
public int AddressID
{
get { return _addressID; }
set { _addressID = value; }
}
private string _street;
public string Street
{
get { return _street; }
set { _street = value; }
}
private string _city;
public string City
{
get { return _city; }
set { _city = value; }
}
}
}
Clone类
Code
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
using System.Collections;
namespace Cloning
{
/**//// <summary>
/// Delegate handler that's used to compile the IL to.
/// (This delegate is standard in .net 3.5)
/// </summary>
/// <typeparam name="T1">Parameter Type</typeparam>
/// <typeparam name="TResult">Return Type</typeparam>
/// <param name="arg1">Argument</param>
/// <returns>Result</returns>
public delegate TResult Func<T1, TResult>(T1 arg1);
public class Cloning
{
/**//// <summary>
/// This dictionary caches the delegates for each 'to-clone' type.
/// </summary>
private static Dictionary<Type, Delegate> _cachedIL = new Dictionary<Type, Delegate>();
private static Dictionary<Type, Delegate> _cachedILDeep = new Dictionary<Type, Delegate>();
private LocalBuilder _lbfTemp;
/**//// <summary>
/// Clone one person object with reflection
/// </summary>
/// <param name="p">Person to clone</param>
/// <returns>Cloned person</returns>
public static Person CloneObjectWithReflection(Person p)
{
FieldInfo[] fis = p.GetType().GetFields(System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic);
Person newPerson = new Person();
foreach (FieldInfo fi in fis)
{
fi.SetValue(newPerson, fi.GetValue(p));
}
return newPerson;
}
/**//// <summary>
/// Clone a person object by manually typing the copy statements.
/// </summary>
/// <param name="p">Object to clone</param>
/// <returns>Cloned object</returns>
public static Person CloneNormal(Person p)
{
Person newPerson = new Person();
newPerson.ID = p.ID;
newPerson.Name = p.Name;
newPerson.FirstName = p.FirstName;
newPerson.Address = new Address();
newPerson.Address.AddressID = p.Address.AddressID;
newPerson.Address.City = p.Address.City;
newPerson.Address.Street = p.Address.Street;
if(newPerson.Addresses!=null)
{
newPerson.Addresses = new List<Address>();
foreach (Address a in newPerson.Addresses)
{
newPerson.Addresses.Add(a);
}
}
return newPerson;
}
/**//// <summary>
/// Generic cloning method that clones an object using IL.
/// Only the first call of a certain type will hold back performance.
/// After the first call, the compiled IL is executed.
/// </summary>
/// <typeparam name="T">Type of object to clone</typeparam>
/// <param name="myObject">Object to clone</param>
/// <returns>Cloned object</returns>
public static T CloneObjectWithILShallow<T>(T myObject)
{
Delegate myExec = null;
if (!_cachedIL.TryGetValue(typeof(T), out myExec))
{
// Create ILGenerator (both DM declarations work)
// DynamicMethod dymMethod = new DynamicMethod("DoClone", typeof(T),
// new Type[] { typeof(T) }, true);
DynamicMethod dymMethod = new DynamicMethod("DoClone", typeof(T),
new Type[] { typeof(T) }, Assembly.GetExecutingAssembly().ManifestModule, true);
ConstructorInfo cInfo = myObject.GetType().GetConstructor(new Type[] { });
ILGenerator generator = dymMethod.GetILGenerator();
LocalBuilder lbf = generator.DeclareLocal(typeof(T));
generator.Emit(OpCodes.Newobj, cInfo);
generator.Emit(OpCodes.Stloc_0);
foreach (FieldInfo field in myObject.GetType().GetFields(
System.Reflection.BindingFlags.Instance
| System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.Public))
{
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, field);
generator.Emit(OpCodes.Stfld, field);
}
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ret);
myExec = dymMethod.CreateDelegate(typeof(Func<T, T>));
_cachedIL.Add(typeof(T), myExec);
}
return ((Func<T, T>)myExec)(myObject);
}
public T CloneObjectWithILDeep<T>(T myObject)
{
Delegate myExec = null;
if (!_cachedILDeep.TryGetValue(typeof(T), out myExec))
{
// Create ILGenerator (both DM declarations work)
// DynamicMethod dymMethod = new DynamicMethod("DoClone", typeof(T),
// new Type[] { typeof(T) }, true);
DynamicMethod dymMethod = new DynamicMethod("DoClone", typeof(T),
new Type[] { typeof(T) }, Assembly.GetExecutingAssembly().ManifestModule, true);
ConstructorInfo cInfo = myObject.GetType().GetConstructor(new Type[] { });
ILGenerator generator = dymMethod.GetILGenerator();
LocalBuilder lbf = generator.DeclareLocal(typeof(T));
generator.Emit(OpCodes.Newobj, cInfo);
generator.Emit(OpCodes.Stloc_0);
foreach (FieldInfo field in typeof(T).GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public))
{
if (field.FieldType.IsValueType || field.FieldType == typeof(string))
CopyValueType(generator, field);
else if (field.FieldType.IsClass)
CopyReferenceType(generator, field);
}
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ret);
myExec = dymMethod.CreateDelegate(typeof(Func<T, T>));
_cachedILDeep.Add(typeof(T), myExec);
}
return ((Func<T, T>)myExec)(myObject);
}
private void CreateNewTempObject(ILGenerator generator, Type type)
{
ConstructorInfo cInfo = type.GetConstructor(new Type[] { });
generator.Emit(OpCodes.Newobj, cInfo);
generator.Emit(OpCodes.Stloc, _lbfTemp);
}
private void CopyValueType(ILGenerator generator, FieldInfo field)
{
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, field);
generator.Emit(OpCodes.Stfld, field);
}
private void CopyValueTypeTemp(ILGenerator generator, FieldInfo fieldParent, FieldInfo fieldDetail)
{
generator.Emit(OpCodes.Ldloc_1);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, fieldParent);
generator.Emit(OpCodes.Ldfld, fieldDetail);
generator.Emit(OpCodes.Stfld, fieldDetail);
}
private void PlaceNewTempObjInClone(ILGenerator generator, FieldInfo field)
{
// Get object from custom location and store it in right field of location 0
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ldloc, _lbfTemp);
generator.Emit(OpCodes.Stfld, field);
}
private void CopyReferenceType(ILGenerator generator, FieldInfo field)
{
// We have a reference type.
_lbfTemp = generator.DeclareLocal(field.FieldType);
if (field.FieldType.GetInterface("IEnumerable") != null)
{
// We have a list type (generic).
if (field.FieldType.IsGenericType)
{
// Get argument of list type
Type argType = field.FieldType.GetGenericArguments()[0];
// Check that it has a constructor that accepts another IEnumerable.
Type genericType = Type.GetType("System.Collections.Generic.IEnumerable`1["
+ argType.FullName + "]");
ConstructorInfo ci = field.FieldType.GetConstructor(new Type[] { genericType });
if (ci != null)
{
// It has! (Like the List<> class)
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, field);
generator.Emit(OpCodes.Newobj, ci);
generator.Emit(OpCodes.Stloc, _lbfTemp);
PlaceNewTempObjInClone(generator, field);
}
}
}
else
{
CreateNewTempObject(generator, field.FieldType);
PlaceNewTempObjInClone(generator, field);
foreach (FieldInfo fi in field.FieldType.GetFields(System.Reflection.BindingFlags.Instance
| System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public))
{
if (fi.FieldType.IsValueType || fi.FieldType == typeof(string))
CopyValueTypeTemp(generator, field, fi);
else if (fi.FieldType.IsClass)
CopyReferenceType(generator, fi);
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
using System.Collections;
namespace Cloning
{
/**//// <summary>
/// Delegate handler that's used to compile the IL to.
/// (This delegate is standard in .net 3.5)
/// </summary>
/// <typeparam name="T1">Parameter Type</typeparam>
/// <typeparam name="TResult">Return Type</typeparam>
/// <param name="arg1">Argument</param>
/// <returns>Result</returns>
public delegate TResult Func<T1, TResult>(T1 arg1);
public class Cloning
{
/**//// <summary>
/// This dictionary caches the delegates for each 'to-clone' type.
/// </summary>
private static Dictionary<Type, Delegate> _cachedIL = new Dictionary<Type, Delegate>();
private static Dictionary<Type, Delegate> _cachedILDeep = new Dictionary<Type, Delegate>();
private LocalBuilder _lbfTemp;
/**//// <summary>
/// Clone one person object with reflection
/// </summary>
/// <param name="p">Person to clone</param>
/// <returns>Cloned person</returns>
public static Person CloneObjectWithReflection(Person p)
{
FieldInfo[] fis = p.GetType().GetFields(System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic);
Person newPerson = new Person();
foreach (FieldInfo fi in fis)
{
fi.SetValue(newPerson, fi.GetValue(p));
}
return newPerson;
}
/**//// <summary>
/// Clone a person object by manually typing the copy statements.
/// </summary>
/// <param name="p">Object to clone</param>
/// <returns>Cloned object</returns>
public static Person CloneNormal(Person p)
{
Person newPerson = new Person();
newPerson.ID = p.ID;
newPerson.Name = p.Name;
newPerson.FirstName = p.FirstName;
newPerson.Address = new Address();
newPerson.Address.AddressID = p.Address.AddressID;
newPerson.Address.City = p.Address.City;
newPerson.Address.Street = p.Address.Street;
if(newPerson.Addresses!=null)
{
newPerson.Addresses = new List<Address>();
foreach (Address a in newPerson.Addresses)
{
newPerson.Addresses.Add(a);
}
}
return newPerson;
}
/**//// <summary>
/// Generic cloning method that clones an object using IL.
/// Only the first call of a certain type will hold back performance.
/// After the first call, the compiled IL is executed.
/// </summary>
/// <typeparam name="T">Type of object to clone</typeparam>
/// <param name="myObject">Object to clone</param>
/// <returns>Cloned object</returns>
public static T CloneObjectWithILShallow<T>(T myObject)
{
Delegate myExec = null;
if (!_cachedIL.TryGetValue(typeof(T), out myExec))
{
// Create ILGenerator (both DM declarations work)
// DynamicMethod dymMethod = new DynamicMethod("DoClone", typeof(T),
// new Type[] { typeof(T) }, true);
DynamicMethod dymMethod = new DynamicMethod("DoClone", typeof(T),
new Type[] { typeof(T) }, Assembly.GetExecutingAssembly().ManifestModule, true);
ConstructorInfo cInfo = myObject.GetType().GetConstructor(new Type[] { });
ILGenerator generator = dymMethod.GetILGenerator();
LocalBuilder lbf = generator.DeclareLocal(typeof(T));
generator.Emit(OpCodes.Newobj, cInfo);
generator.Emit(OpCodes.Stloc_0);
foreach (FieldInfo field in myObject.GetType().GetFields(
System.Reflection.BindingFlags.Instance
| System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.Public))
{
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, field);
generator.Emit(OpCodes.Stfld, field);
}
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ret);
myExec = dymMethod.CreateDelegate(typeof(Func<T, T>));
_cachedIL.Add(typeof(T), myExec);
}
return ((Func<T, T>)myExec)(myObject);
}
public T CloneObjectWithILDeep<T>(T myObject)
{
Delegate myExec = null;
if (!_cachedILDeep.TryGetValue(typeof(T), out myExec))
{
// Create ILGenerator (both DM declarations work)
// DynamicMethod dymMethod = new DynamicMethod("DoClone", typeof(T),
// new Type[] { typeof(T) }, true);
DynamicMethod dymMethod = new DynamicMethod("DoClone", typeof(T),
new Type[] { typeof(T) }, Assembly.GetExecutingAssembly().ManifestModule, true);
ConstructorInfo cInfo = myObject.GetType().GetConstructor(new Type[] { });
ILGenerator generator = dymMethod.GetILGenerator();
LocalBuilder lbf = generator.DeclareLocal(typeof(T));
generator.Emit(OpCodes.Newobj, cInfo);
generator.Emit(OpCodes.Stloc_0);
foreach (FieldInfo field in typeof(T).GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public))
{
if (field.FieldType.IsValueType || field.FieldType == typeof(string))
CopyValueType(generator, field);
else if (field.FieldType.IsClass)
CopyReferenceType(generator, field);
}
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ret);
myExec = dymMethod.CreateDelegate(typeof(Func<T, T>));
_cachedILDeep.Add(typeof(T), myExec);
}
return ((Func<T, T>)myExec)(myObject);
}
private void CreateNewTempObject(ILGenerator generator, Type type)
{
ConstructorInfo cInfo = type.GetConstructor(new Type[] { });
generator.Emit(OpCodes.Newobj, cInfo);
generator.Emit(OpCodes.Stloc, _lbfTemp);
}
private void CopyValueType(ILGenerator generator, FieldInfo field)
{
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, field);
generator.Emit(OpCodes.Stfld, field);
}
private void CopyValueTypeTemp(ILGenerator generator, FieldInfo fieldParent, FieldInfo fieldDetail)
{
generator.Emit(OpCodes.Ldloc_1);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, fieldParent);
generator.Emit(OpCodes.Ldfld, fieldDetail);
generator.Emit(OpCodes.Stfld, fieldDetail);
}
private void PlaceNewTempObjInClone(ILGenerator generator, FieldInfo field)
{
// Get object from custom location and store it in right field of location 0
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ldloc, _lbfTemp);
generator.Emit(OpCodes.Stfld, field);
}
private void CopyReferenceType(ILGenerator generator, FieldInfo field)
{
// We have a reference type.
_lbfTemp = generator.DeclareLocal(field.FieldType);
if (field.FieldType.GetInterface("IEnumerable") != null)
{
// We have a list type (generic).
if (field.FieldType.IsGenericType)
{
// Get argument of list type
Type argType = field.FieldType.GetGenericArguments()[0];
// Check that it has a constructor that accepts another IEnumerable.
Type genericType = Type.GetType("System.Collections.Generic.IEnumerable`1["
+ argType.FullName + "]");
ConstructorInfo ci = field.FieldType.GetConstructor(new Type[] { genericType });
if (ci != null)
{
// It has! (Like the List<> class)
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, field);
generator.Emit(OpCodes.Newobj, ci);
generator.Emit(OpCodes.Stloc, _lbfTemp);
PlaceNewTempObjInClone(generator, field);
}
}
}
else
{
CreateNewTempObject(generator, field.FieldType);
PlaceNewTempObjInClone(generator, field);
foreach (FieldInfo fi in field.FieldType.GetFields(System.Reflection.BindingFlags.Instance
| System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public))
{
if (fi.FieldType.IsValueType || fi.FieldType == typeof(string))
CopyValueTypeTemp(generator, field, fi);
else if (fi.FieldType.IsClass)
CopyReferenceType(generator, fi);
}
}
}
}
}
测试程序:
namespace Cloning
{
class Program
{
static void Main(string[] args)
{
// Do some cloning tests
Cloning.TestCloning tc = new Cloning.TestCloning();
tc.DoTest();
}
}
}
{
class Program
{
static void Main(string[] args)
{
// Do some cloning tests
Cloning.TestCloning tc = new Cloning.TestCloning();
tc.DoTest();
}
}
}
还有一篇:Rick Minerich - A .NET Assembly for Cloning Objects with Arbitrary Field Value Changes: IcManipluator
Update 2008-12-26: