65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 中用于克隆对象的基类

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.55/5 (36投票s)

2002年12月30日

2分钟阅读

viewsIcon

552715

downloadIcon

2113

这个类为你实现了 ICloneable 接口。

引言

虽然现实世界中克隆是一个有争议的话题,但在 .NET 世界中,它仍然可以安全使用,或者说,真的可以吗?

你多少次发现自己为你的类实现 ICloneable 接口,但每次都写相同的代码,或者为每个类编写特定的代码。另外,当你向类中添加一个新字段时,却忘记更新 Clone 方法以适应这个新字段,该怎么办?相信我,这种事情会导致令人讨厌的错误。

这时,我的类就派上用场了。借助反射机制,我创建了一个抽象类,它实现了 ICloneable 接口,并提供了默认行为。你可能想问:默认行为是什么?我很高兴你问了。克隆的默认行为是,通过以下算法克隆类中的每个字段:

  1. 对于类中的每个字段,询问它是否支持 ICloneable 接口。
  2. 如果该字段不支持 ICloneable 接口,则以常规方式设置该字段,这意味着如果该字段是值类型,则将复制该值,但如果该字段是引用类型,则克隆字段将指向相同的对象。
  3. 如果该字段支持 ICloneable 接口,我们使用它的 Clone 方法将其设置为克隆对象中的值。
  4. 如果该字段支持 IEnumerable 接口,则需要检查它是否支持 IListIDictionary 接口。如果支持,则遍历集合,并对集合中的每个项目询问它是否支持 ICloneable 接口。

如何使用

要使你的类支持 ICloneable 接口,只需将你的类从 BaseObject 派生,如下所示:

public class MyClass : BaseObject
{
    public string myStr =”test”;
    public int id;
}

public class MyContainer : BaseObject
{
    public string name = “test2”;
    public MyClass[] myArray= new MyClass[5];

    public class MyContainer()
    {
        for(int i=0 ; i<5 ; i++)
        {
             this.myArray[I] = new MyClass();
        }
    }
}

现在在 Main 方法中,你可以执行以下操作:

static void Main(string[] args)
{
    MyContainer con1 = new MyContainer();
    MyContainer con2 = (MyContainer)con1.Clone();

   con2.myArray[0].id = 5;
}

检查 con2 实例时,你将看到第一个索引中的 MyClass 实例已更改为 5,但 con1 实例保持不变。因此,你可以看到,你添加到类中的任何支持 ICloneable 接口的字段都将被克隆。此外,如果该字段支持 IList 接口或 IDictionary 接口,该方法将检测到它并循环遍历所有项目并尝试克隆它们。

实现

/// <summary>
/// BaseObject class is an abstract class for you to derive from.
/// Every class that will be dirived from this class will support the 
/// Clone method automaticly.
/// The class implements the interface ICloneable and there /// for every object that will be derived
/// from this object will support the ICloneable interface as well. /// </summary> public abstract class BaseObject : ICloneable { /// <summary> /// Clone the object, and returning a reference to a cloned object. /// </summary> /// <returns>Reference to the new cloned /// object.</returns> public object Clone() { //First we create an instance of this specific type. object newObject = Activator.CreateInstance( this.GetType() ); //We get the array of fields for the new type instance. FieldInfo[] fields = newObject.GetType().GetFields(); int i = 0; foreach( FieldInfo fi in this.GetType().GetFields() ) { //We query if the fiels support the ICloneable interface. Type ICloneType = fi.FieldType. GetInterface( "ICloneable" , true ); if( ICloneType != null ) { //Getting the ICloneable interface from the object. ICloneable IClone = (ICloneable)fi.GetValue(this); //We use the clone method to set the new value to the field. fields[i].SetValue( newObject , IClone.Clone() ); } else { // If the field doesn't support the ICloneable // interface then just set it. fields[i].SetValue( newObject , fi.GetValue(this) ); } //Now we check if the object support the //IEnumerable interface, so if it does //we need to enumerate all its items and check if //they support the ICloneable interface. Type IEnumerableType = fi.FieldType.GetInterface ( "IEnumerable" , true ); if( IEnumerableType != null ) { //Get the IEnumerable interface from the field. IEnumerable IEnum = (IEnumerable)fi.GetValue(this); //This version support the IList and the //IDictionary interfaces to iterate on collections. Type IListType = fields[i].FieldType.GetInterface ( "IList" , true ); Type IDicType = fields[i].FieldType.GetInterface ( "IDictionary" , true ); int j = 0; if( IListType != null ) { //Getting the IList interface. IList list = (IList)fields[i].GetValue(newObject); foreach( object obj in IEnum ) { //Checking to see if the current item //support the ICloneable interface. ICloneType = obj.GetType(). GetInterface( "ICloneable" , true ); if( ICloneType != null ) { //If it does support the ICloneable interface, //we use it to set the clone of //the object in the list. ICloneable clone = (ICloneable)obj; list[j] = clone.Clone(); } //NOTE: If the item in the list is not //support the ICloneable interface then in the //cloned list this item will be the same //item as in the original list //(as long as this type is a reference type). j++; } } else if( IDicType != null ) { //Getting the dictionary interface. IDictionary dic = (IDictionary)fields[i]. GetValue(newObject); j = 0; foreach( DictionaryEntry de in IEnum ) { //Checking to see if the item //support the ICloneable interface. ICloneType = de.Value.GetType(). GetInterface( "ICloneable" , true ); if( ICloneType != null ) { ICloneable clone = (ICloneable)de.Value; dic[de.Key] = clone.Clone(); } j++; } } } i++; } return newObject; } }
© . All rights reserved.