• c#拷贝


    话谈c#拷贝

    c#中类型分为值类型和引用类型,值类型对象赋值是本身就是赋的自身的一个副本,而引用类型赋值时则赋的是指向堆上的内存,假如我们不想赋这个地址而想将对象赋过去要怎么做呢?首先要知道拷贝分为浅表拷贝和深层拷贝,浅表拷贝得到一个新的实例,一个与原始对象类型相同、值类型字段相同的拷贝。但是,如果字段是引用类型的,则拷贝的是该引用, 而不是的对象。若想将引用字段的对象也拷贝过去,则称为深拷贝。 为了实现拷贝,本文总结了以下几种方法。

    1.首先就是最笨的方法,传说中的“人工拷贝”,就是将引用里的所有值对象和具有值特征的string对象一一赋给新对象,这种方式代码量过大而且维护起来相当麻烦,所以能不用就不用。

    2.System.Object提供了受保护的方法 MemberwiseClone,可用来实现“浅表”拷贝。由于该方法标记为“受保护”级别,因此,我们只能在继承类或该类内部才能访问该方法。

    复制代码
     public class A
        {
            public string rr { get; set; }
            public string tt { get; set; }
    
    
            public A ShallowCopy()
            {
                return (A)this.MemberwiseClone();
            }
    
        }
    复制代码

    3.使用序列化与反序列化的方式,这种方式虽可实现深度拷贝,但有点大炮打蚊子的味道,而且在外面引用时一定要记得关闭所创建的MemoryStream

    复制代码
     public static object Clone(object o,out MemoryStream ms)
            {
                BinaryFormatter bf = new BinaryFormatter();
               
                ms = new MemoryStream();
                bf.Serialize(ms,o);
                ms.Seek(0, SeekOrigin.Begin);
    
                return bf.Deserialize(ms);
            }
    复制代码

    4.在一个外国人写的博客中(http://www.codeproject.com/Articles/3441/Base-class-for-cloning-an-object-in-C),使用反射的方法来解决了这个问题。他写了一个BaseObject类,如果我们继承这个类就可以实现深度拷贝,下面是他的实现方法:

    创建一个实现 ICloneable 接口的有默认行为的抽象类,所谓的默认行为就是使用以下库函数来拷贝类里的每一个字段。

    1.遍历类里的每个字段,看看是否支持ICloneable接口。

    2.如果不支持ICloneable接口,按下面规则进行设置,也就是说如果字段是个值类型,那将其拷贝,如果是引用类型则拷贝字段指向通一个对象。

    3.如果支持ICloneable,在科隆对象中使用它的克隆方法进行设置。

    4.如果字段支持IEnumerable接口,需要看看它是否支持IList或者IDirectionary接口,如果支持,迭代集合看看是否支持ICloneable接口。

    我们所要做的就是使得所有字段支持ICloneable接口。

    下面是测试结果:

    复制代码
    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();
            }
        }
    }
    复制代码
    复制代码
    static void Main(string[] args)
    {
        MyContainer con1 = new MyContainer();
        MyContainer con2 = (MyContainer)con1.Clone();
    
       con2.myArray[0].id = 5;
    }
    复制代码

    con2中MyClass实例中第一个元素变成了5,但con1没有改变,即实现了深拷贝。

    BaseObject的实现:

    复制代码
    /// <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.<br>
    /// The class implements the interface ICloneable and there 
    /// for every object that will be derived <br>
    /// 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;
        }
    }
    复制代码
    浮躁的人容易问:我到底该学什么;----别问,学就对了; 浮躁的人容易问:JS有钱途吗;----建议你去抢银行; 浮躁的人容易说:我要中文版!我英文不行!----不行?学呀! 浮躁的人分两种:只观望而不学的人;只学而不坚持的人; 浮躁的人永远不是一个高手。
     
    分类: C#
  • 相关阅读:
    Ubuntu:Failed to restart network.service: Unit network.service not found.
    解决 yarn或pnpm : 无法加载文件 C:\Users\hp\AppData\Roaming\npm\cnpm.ps1,因为在此系统上禁止运行脚本
    MSSQL表名、列名转大写SQL语句
    js 控制打开网页窗口的大小位置 sk
    js实现公历(阳历)和农历(阴历)的换算 sk
    一个怂女婿的成长笔记【二十四】
    牛顿迭代法(大白话)
    Source Generator实战
    i=i++
    记录下三种排序,冒泡,选择,和快速。
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3528954.html
Copyright © 2020-2023  润新知