使用IL实现对象的Clone,浅拷贝,和深拷贝的几篇文章

本文探讨了在C#中实现对象克隆的不同方法,包括使用反射和中间语言(IL)进行浅拷贝和深拷贝。通过示例代码演示了如何创建高效的克隆机制,并对各种方法进行了性能对比。

对于实现任意对象的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; }
        }


    }

}



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);
                }

            }

        }


    }

}



测试程序:
namespace Cloning
{
   
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:

Object Deep Cloning using IL in C# - version 1.1

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值