C#中的深复制和浅复制(在C#中克隆对象) 整理

   
http://www.codeproject.com/KB/cs/cloneimpl_class.aspx
 
 

Introduction

Although the subject of cloning in the real world is controversial, in the .NET world it is still safe to use, or isn�t it?

How many times did you find yourself implementing the ICloneable interface for your class, but every time you do the same code, or you do a specific code for each class. And what about, when you add a new field to the class, and you forgot to update the Clone method for the new field. Believe me, this sort of thing leads to annoying bugs.

This is where my class comes to the rescue. With a little help from the reflection mechanism, I created an abstract class that implements the ICloneable interface with the default behavior. Now you are probably asking yourself: What is the default behavior? Well I�m glad you asked. Default behavior for cloning, is to clone every field in the class by the following algorithm:

  1. For each field in the class, ask if it supports the ICloneable interface.
  2. If the field doesn�t support the ICloneable interface, then the field is set in the regular manner, which means that if this field is a value type, then the value will be copied, but if the field is a reference type, the clone field will be pointing to the same object.
  3. If the field supports the ICloneable interface, we use its Clone method to set it in the clone object.
  4. If the field supports the IEnumerable interface, then we need to check if it supports the IList or the IDictionary interface. If it does, then we iterate the collection, and for each item in the collection we ask if it supports the ICloneable interface.

How to use

All you have to do to make your class support the ICloneable interface, is to derive your class from the BaseObject as follow:

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

Now in the Main method you can do the following:

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

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

When inspecting the con2 instance you will see that the MyClass instance in the first index was changed to 5, but the con1 instance remained without changes. So you can see that any field you will add to your class, which support the ICloneable interface will be cloned as well. Furthermore, if the field supports the IList interface or the IDictionary interface, the method will detect it and will loop through all the items and will try to clone them as well.

Implementation

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

C#中的深复制和浅复制(在C#中克隆对象)

C# 支持两种类型:“值类型”和“引用类型”。 值类型(Value Type)(如 char、int 和 float)、枚举类型和结构类型。 引用类型(Reference Type) 包括类 (Class) 类型、接口类型、委托类型和数组类型。

如何来划分它们? 以它们在计算机内存中如何分配来划分

值类型与引用类型的区别? 1,值类型的变量直接包含其数据, 2,引用类型的变量则存储对象引用。 对于引用类型,两个变量可能引用同一个对象,因此对一个变量的操作可能影响另一个变量所引用的对象。对于值类型,每个变量都有自己的数据副本,对一个变量的操作不可能影响另一个变量。

值类型隐式继承自System.ValueType  所以不能显示让一个结构继承一个类,C#不支持多继承

堆栈(stack)是一种先进先出的数据结构,在内存中,变量会被分配在堆栈上来进行操作。 堆(heap)是用于为类型实例(对象)分配空间的内存区域,在堆上创建一个对象, 会将对象的地址传给堆栈上的变量(反过来叫变量指向此对象,或者变量引用此对象)。

关于对象克隆的所设计到知识点

浅拷贝:是指将对象中的所有字段逐字复杂到一个新对象         对值类型字段只是简单的拷贝一个副本到目标对象,改变目标对象中值类型字段的值不会反映到原始对象中,因为拷贝的是副本         对引用型字段则是指拷贝他的一个引用到目标对象。改变目标对象中引用类型字段的值它将反映到原始对象中,因为拷贝的是指向堆是上的一个地址

深拷贝:深拷贝与浅拷贝不同的是对于引用字段的处理,深拷贝将会在新对象中创建一个新的对象和         原始对象中对应字段相同(内容相同)的字段,也就是说这个引用和原始对象的引用是不同, 我们改变新         对象中这个字段的时候是不会影响到原始对象中对应字段的内容。

浅复制: 实现浅复制需要使用Object类的MemberwiseClone方法用于创建一个浅表副本 深复制: 须实现 ICloneable接口中的Clone方法,且需要需要克隆的对象加上[Serializable]特性

 

 

class DrawBase:System.Object , ICloneable     {         public string name = "jmj";         public DrawBase()         {         }                 public object Clone()         {             return this as object;      //引用同一个对象             return this.MemberwiseClone(); //浅复制             return new DrawBase() as object;//深复制         }     } class Program {           static void Main(string[] args)         {             DrawBase rect = new DrawBase();             Console.WriteLine(rect.name);             DrawBase line = rect.Clone() as DrawBase;             line.name = "a9fs3";             Console.WriteLine(rect.name);             DrawBase ploy = line.Clone() as DrawBase;             ploy.name = "lj";             Console.WriteLine(rect.name);               Console.WriteLine(object.ReferenceEquals(line, ploy));             Console.ReadLine();         } } 运行结果:  return this as object;      //引用同一个对象 输出:jmj         a9fs3         lj         True   return this.MemberwiseClone(); //浅复制 return new DrawBase() as object;//深复制 输出均为: jmj              jmj              jmj              False 解释: return this as object 方法总是引用同一个对象,因此相应的堆内存上的值会改变! 后两种方法都是对对象的复制,区别在于复制的类别不同:深复制会复制整个填充的对象,包括该对象中其他引用类型和值类型的值;而浅复制只复制了一个对象中所有引用,它没有值的复制,通过引用它们的其他对象的引用来共享它们。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值