一文搞懂C#中的赋值、深复制、浅复制

一、文字含义

先引入一个观念,赋值和深复制、浅复制并不是一样的,含义是不一样的。

本文所说的主要是针对“ 引用类型 ”本文以 “类 ”为例加以说明。一般的系统定义的值类型(int、double、float等等)此处不做考虑。

1、赋值。指的是 “ 等号= ”。它相当于是给引用对象起一个别名。

2、浅度复制和深度复制。指的是类实现 ICloneable接口,重写该接口的唯一方法。注意:不管是深度复制还是浅度复制,都是通过ICloneable接口去实现的。

二、实例代码

1、赋值

此处定义了一个父亲类Parent,他有三个属性 年龄Age,身高Height,孩子信息child,但是孩子信息又包含三个信息,即姓名Name,年龄Age,体重Weight。对于父类Parent而言,Age、Height是“ 值类型 ”,而成员child是“ 引用类型 ”。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 赋值_深度复制_浅度复制
{
    class Program
    {
        static void Main(string[] args)
        {
            Parent parent_01 = new Parent();
            parent_01.Age = 48;
            parent_01.Height = 173;
            parent_01.child = new Children("菲菲", 25, 55.55);
            Console.WriteLine("年龄:{0} 身高:{1} 孩子的信息为:{2} {3} {4}", parent_01.Age, parent_01.Height, parent_01.child.Name, parent_01.child.Age, parent_01.child.Weight);
            Parent parent_02 = parent_01;
            Console.WriteLine("年龄:{0} 身高:{1} 孩子的信息为:{2} {3} {4}", parent_02.Age, parent_02.Height, parent_02.child.Name, parent_02.child.Age, parent_02.child.Weight);
            Console.WriteLine("===========================================");
            parent_02.Age = 60;
            parent_02.Height = 189;
            parent_02.child.Age = 35;
            parent_02.child.Name = "宝宝";
            Console.WriteLine("年龄:{0} 身高:{1} 孩子的信息为:{2} {3} {4}", parent_01.Age, parent_01.Height, parent_01.child.Name, parent_01.child.Age, parent_01.child.Weight);
            Console.WriteLine("年龄:{0} 身高:{1} 孩子的信息为:{2} {3} {4}", parent_02.Age, parent_02.Height, parent_02.child.Name, parent_02.child.Age, parent_02.child.Weight);
        }
    }
    class Parent
    {
        private int age;
        private double height;
        public Children child;

        public Parent()
        {

        }

        public int Age { get => age; set => age = value; }
        public double Height { get => height; set => height = value; }
    }
    class Children
    {
        private string name;
        private int age;
        private double weight;
        public Children(string name,int age,double weight)
        {
            this.name = name;
            this.age = age;
            this.weight = weight;
        }

        public string Name { get => name; set => name = value; }
        public int Age { get => age; set => age = value; }
        public double Weight { get => weight; set => weight = value; }
    }
}

运行结果如下:

年龄:48 身高:173 孩子的信息为:菲菲 25 55.55
年龄:48 身高:173 孩子的信息为:菲菲 25 55.55
===========================================
年龄:60 身高:189 孩子的信息为:宝宝 35 55.55
年龄:60 身高:189 孩子的信息为:宝宝 35 55.55
请按任意键继续. . .

===================================================================

总结:parent_01和parent_02完全是共用的,不管是引用类型成员,还是值类型成员,都是共用的,parent_02任何成员改变,parent_01都会跟着改变。

2、浅复制

给父类Parent实现ICloneable接口,重写Clone方法,注意,默认的MemberWiseClone()就是系统实现的“ 浅度复制 ”。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 浅复制
{
    class Program
    {
        static void Main(string[] args)
        {
            Parent parent_01 = new Parent();
            parent_01.Age = 48;
            parent_01.Height = 173;
            parent_01.child = new Children("菲菲", 25, 55.55);
            Console.WriteLine("年龄:{0} 身高:{1} 孩子的信息为:{2} {3} {4}", parent_01.Age, parent_01.Height, parent_01.child.Name, parent_01.child.Age, parent_01.child.Weight);
            Parent parent_02 = parent_01.Clone() as Parent;
            Console.WriteLine("年龄:{0} 身高:{1} 孩子的信息为:{2} {3} {4}", parent_02.Age, parent_02.Height, parent_02.child.Name, parent_02.child.Age, parent_02.child.Weight);
            Console.WriteLine("===========================================");
            parent_02.Age = 60;
            parent_02.Height = 189;
            parent_02.child.Age = 35;
            parent_02.child.Name = "宝宝";
            Console.WriteLine("年龄:{0} 身高:{1} 孩子的信息为:{2} {3} {4}", parent_01.Age, parent_01.Height, parent_01.child.Name, parent_01.child.Age, parent_01.child.Weight);
            Console.WriteLine("年龄:{0} 身高:{1} 孩子的信息为:{2} {3} {4}", parent_02.Age, parent_02.Height, parent_02.child.Name, parent_02.child.Age, parent_02.child.Weight);
        }
    }
    class Parent:ICloneable
    {
        private int age;
        private double height;
        public Children child;

        public Parent()
        {

        }

        public int Age { get => age; set => age = value; }
        public double Height { get => height; set => height = value; }
        public object Clone()
        {
            return this.MemberwiseClone();  //浅复制
        }
    }
    class Children
    {
        private string name;
        private int age;
        private double weight;
        public Children(string name, int age, double weight)
        {
            this.name = name;
            this.age = age;
            this.weight = weight;
        }

        public string Name { get => name; set => name = value; }
        public int Age { get => age; set => age = value; }
        public double Weight { get => weight; set => weight = value; }
    }
}

运行结果为:

年龄:48 身高:173 孩子的信息为:菲菲 25 55.55
年龄:48 身高:173 孩子的信息为:菲菲 25 55.55
===========================================
年龄:48 身高:173 孩子的信息为:宝宝 35 55.55
年龄:60 身高:189 孩子的信息为:宝宝 35 55.55
请按任意键继续. . .

总结:从上可以看出,对于父亲类Parent的值类型成员,parent_01和parent_02是不共用的,parent_02改变,parent_01独立的,并不会改变;但是对于引用类型成员child,parent_01和parent_02是共用的,parent_02改变,parent_01也会跟着改变。

3、深度复制

深度复制的实现和浅度复制类似,只不过是重载的Clone方法实现不一样,具体可参见代码。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 深复制
{
    class Program
    {
        static void Main(string[] args)
        {
            Parent parent_01 = new Parent();
            parent_01.Age = 48;
            parent_01.Height = 173;
            parent_01.child = new Children("菲菲", 25, 55.55);
            Console.WriteLine("年龄:{0} 身高:{1} 孩子的信息为:{2} {3} {4}", parent_01.Age, parent_01.Height, parent_01.child.Name, parent_01.child.Age, parent_01.child.Weight);
            Parent parent_02 = parent_01.Clone() as Parent;
            Console.WriteLine("年龄:{0} 身高:{1} 孩子的信息为:{2} {3} {4}", parent_02.Age, parent_02.Height, parent_02.child.Name, parent_02.child.Age, parent_02.child.Weight);
            Console.WriteLine("===========================================");
            parent_02.Age = 60;
            parent_02.Height = 189;
            parent_02.child.Age = 35;
            parent_02.child.Name = "宝宝";
            Console.WriteLine("年龄:{0} 身高:{1} 孩子的信息为:{2} {3} {4}", parent_01.Age, parent_01.Height, parent_01.child.Name, parent_01.child.Age, parent_01.child.Weight);
            Console.WriteLine("年龄:{0} 身高:{1} 孩子的信息为:{2} {3} {4}", parent_02.Age, parent_02.Height, parent_02.child.Name, parent_02.child.Age, parent_02.child.Weight);
        }
    }
    class Parent : ICloneable
    {
        private int age;
        private double height;
        public Children child;

        public Parent()
        {

        }

        public int Age { get => age; set => age = value; }
        public double Height { get => height; set => height = value; }
        public object Clone()
        {
            Parent newparent = new Parent();
            newparent.Age = 48;newparent.Height = 173;
            newparent.child = new Children("菲菲", 25, 55.55);
            Object newParent = newparent;
            return newParent;  //深复制
        }
    }
    class Children
    {
        private string name;
        private int age;
        private double weight;
        public Children(string name, int age, double weight)
        {
            this.name = name;
            this.age = age;
            this.weight = weight;
        }

        public string Name { get => name; set => name = value; }
        public int Age { get => age; set => age = value; }
        public double Weight { get => weight; set => weight = value; }
    }
}

运行结果为:

年龄:48 身高:173 孩子的信息为:菲菲 25 55.55
年龄:48 身高:173 孩子的信息为:菲菲 25 55.55
===========================================
年龄:48 身高:173 孩子的信息为:菲菲 25 55.55
年龄:60 身高:189 孩子的信息为:宝宝 35 55.55
请按任意键继续. . .

总结:从上可以看出,对于父亲类Parent的所有成员,parent_01和parent_02是完全不共用的,parent_02改变,不管是改变父亲类Parent的值类型成员,还是应用类型成员,parent_01都是独立的,并不会改变。

三、总结

1、赋值。赋值和深度复制,浅度复制完全是不同的概念,并没有什么关系,很多文章说赋值对于值类型是深度复制,对于引用类型是浅度复制,这种说法是不正确的,它的本质是在线程栈上产生一样的副本。

2、浅度复制。值类型成员独立,但是引用类型成员共享。

3、深度复制。值类型成员和引用类型成员都是独立的,即完完全全的一个全新的副本,称之为深度复制。

有兴趣的可以参考python的深度复制和浅度复制,有类似的地方。

猜你喜欢

转载自blog.csdn.net/qq_27825451/article/details/81274668