1、浅拷贝与深拷贝的定义
  什么是拷贝?拷贝即为常说的复制或者克隆一个对象,并且通过拷贝这些源对象创建新的对象。其中拷贝分为浅拷贝和深拷贝。对于拷贝出来的对象,在使用上有很大的差异,特别是在引用类型上。
浅拷贝:将对象中的所有字段复制到新的对象中。其中,值类型字段被复制到新对象中后,在新对象中的修改不会影响到原先对象的值。而新对象的引用类型则是原先对象引用类型的引用,不是引用自己对象本身。注:在新对象中修改引用类型的值会影响到原先对象;理论上String也是引用类型,但是由于由于该类型比较特殊,Object.MemberwiseClone()方法依旧为其新对象开辟了新的内存空间存储String的值,在浅拷贝中把String类型当作'值类型'即可。
深拷贝:同样也是拷贝,但是与浅拷贝不同的是,深拷贝会对引用类型重新在创新一次(包括值类型),在新对象做的任何修改都不会影响到源对象本身。
2、实现浅拷贝与深拷贝
在 .Net平台开发中,要实现拷贝,微软官方建议继承ICloneable接口,该接口位于System命名空间下,该接口只实现一个Clone方法,我们可以根据具体项目需求在该方法内实现浅拷贝或者深拷贝。先实现一个浅拷贝,具体代码如下:
 //Equal探索
        static void Main()
        {
            //创建源对象
            Teacher Source = new Teacher("Fode",18,DateTime.Now,22);
            Source.Print("源对象");
            //浅拷贝对象
            Teacher Target = Source.Clone() as Teacher;
            /*
             理论上String也是引用类型,但是由于由于该类型比较特殊,
             Object.MemberwiseClone()方法依旧为其新对象开辟了新的内存空间存储String的值,
             在浅拷贝中把String类型当作'值类型'即可
             */
            Target.Name = "JJ";
            Target.Student.Count = 11;
            Console.WriteLine("新对象的引用类型的值发生变化");
            Target.Print("新对象");
            Source.Print("源对象");
            Console.ReadKey();
        }
        class Teacher : ICloneable
        {
            public Teacher(String name, Int32 age, DateTime birthday, Int32 count)
            {
                this._name = name;
                this._age = age;
                this._birthday = birthday;
                this.Student = new Student() { Count = count };
            }
            private Int32 _age;
            public Int32 Age { get { return _age; } set { _age = value; } }
            private String _name;
            public String Name { get { return _name; } set { _name = value; } }
            private DateTime _birthday;
            public DateTime Birthday { get { return _birthday; } set { _birthday = value; } }
            public Student Student { get; set; }
            public void Print(String title)
            {
                Console.WriteLine(title);
                Console.WriteLine($"基本信息:姓名:{this.Name},年龄:{this.Age},生日:{this.Birthday.ToString("D")}");
                Console.WriteLine($"引用类型的值{Student.ToString()}");
                Console.WriteLine();
            }
            //实现浅拷贝
            public Object Clone()
            {
                return this.MemberwiseClone();
            }
        }
        class Student
        {
            public Int32 Count { get; set; }
            public override string ToString()
            {
                return Count.ToString();
            }
        }
其运行结果如下:

可以发现当新对象的引用类型发生改变后,其源对象的引用类型也发生改变(String类型除外),他们共同引用的是Student这个引用类型对象(即使发生变化的是其里面的值类型),而新对象的值类型改变并不会到源类型的值类型。
而对于要实现深拷贝则有很多中方法了,比如在拷贝方法里面 直接一个个属性字段赋值,但是一旦为源对象新增属性或者字段的时候,容易忘了修改拷贝方法中的值,最好使用序列化的方法进行深拷贝。深拷贝简单代码如下,与浅拷贝同样的案例,只是重写了Clone()方法,并在类加了[Serializable]序列化特性标签:
     [Serializable]
        class Teacher : ICloneable
        {
            public Teacher(String name, Int32 age, DateTime birthday, Int32 count)
            {
                this._name = name;
                this._age = age;
                this._birthday = birthday;
                this.Student = new Student() { Count = count };
            }
            private Int32 _age;
            public Int32 Age { get { return _age; } set { _age = value; } }
            private String _name;
            public String Name { get { return _name; } set { _name = value; } }
            private DateTime _birthday;
            public DateTime Birthday { get { return _birthday; } set { _birthday = value; } }
            public Student Student { get; set; }
            public void Print(String title)
            {
                Console.WriteLine(title);
                Console.WriteLine($"基本信息:姓名:{this.Name},年龄:{this.Age},生日:{this.Birthday.ToString("D")}");
                Console.WriteLine($"引用类型的值{Student.ToString()}");
                Console.WriteLine();
            }
            //实现深拷贝拷贝
            public Object Clone()
            {
                System.IO.Stream stream = new MemoryStream();
                try
                {
                    System.Runtime.Serialization.IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                    formatter.Serialize(stream, this);
                    stream.Seek(0, SeekOrigin.Begin);
                    return formatter.Deserialize(stream);
                }
                finally {
                    stream.Close();
                    stream.Dispose();
                }
            }
        }
        [Serializable]
        class Student
        {
            public Int32 Count { get; set; }
            public override string ToString()
            {
                return Count.ToString();
            }
        }
        static void Main()
        {
            //创建源对象
            Teacher Source = new Teacher("Fode", 18, DateTime.Now, 22);
            Source.Print("源对象");
            //深拷贝对象
            Teacher Target = Source.Clone() as Teacher;
            Target.Name = "JJ";
            Target.Student.Count = 11;
            Console.WriteLine("新对象的引用类型的值发生变化");
            Target.Print("新对象");
            Source.Print("源对象");
            Console.ReadKey();
        }
其结果如下:

可以发现,此时拷贝后的Target对象与源对象没有任何关系。修改源对象的引用类型并不会影响对应新对象的值。最后在把代码优化一下,在一个类中同时实现深拷贝和浅拷贝:
            //实现浅拷贝
            public Object Clone()
            {
                return this.MemberwiseClone();
            }
            /// <summary>
            /// 获得浅拷贝对象
            /// </summary>
            /// <returns></returns>
            public Teacher ShallowClone()
            {
                return this.Clone() as Teacher;
            }
            /// <summary>
            /// 获得深拷贝对象
            /// </summary>
            /// <returns></returns>
            public Teacher DeepClone()
            {
                System.IO.Stream stream = new MemoryStream();
                try
                {
                    System.Runtime.Serialization.IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                    formatter.Serialize(stream, this);
                    stream.Seek(0, SeekOrigin.Begin);
                    return formatter.Deserialize(stream) as Teacher;
                }
                finally
                {
                    stream.Close();
                    stream.Dispose();
                }
            }
注:ICloneable接口适用于
.NET Core
.NET Framework
.NET Standard
Xamarin.Android
Xamarin.iOS
Xamarin.Mac
            Teacher Source = new Teacher("Fode", 18, DateTime.Now, 22);
            Teacher Target = Source;
            Source.Print("源对象");
            Console.WriteLine("源对象的值类型发生改变");
            Source.Name = "JJ"; Source.Age = 22;
            Source.Print("源对象");
            Target.Print("新对象");
            Console.WriteLine("新对象的值类型发生改变");
            Target.Name = "范冰冰"; Target.Age = 18;
            Source.Print("源对象");
            Target.Print("新对象");
            Console.ReadKey();    
输出结果如下:

来源:https://www.cnblogs.com/fode/p/10073675.html