zoukankan      html  css  js  c++  java
  • C# 浅拷贝和深拷贝的实现

    拷贝(复制)为对象创建副本,即将对象中的所有字段复制到新的对象(副本中)。拷贝有两种:浅拷贝和深拷贝,微软建议用类型继承ICloneable接口的方式明确该类型是可以被拷贝的,ICloneable接口只提供了一个Clone方法,需要根据需要在Clone方法内实现浅拷贝或深拷贝。

    1、浅拷贝:把源对象中的值类型字段的引用类型字段的引用复制到副本中。在源对象(副本)中,修改值类型字段的值不会影响到副本(源对象),而修改引用类型字段的值会影响到副本(源对象)。

    注意string类型除外,虽然string类型是引用类型,但是由于该引用类型的特殊性,在浅拷贝过程,副本中会创建新的字符串并把对应的值复制过来,字符串应被看成值类型。

    浅拷贝声明代码,使用Object.MeberwiseClone方法进行浅拷贝:

     1     class Employee : ICloneable
     2     {
     3         public string ID { get; set; }
     4         public int Age { get; set; }
     5         public Department DepartmentName { get; set; }
     6 
     7         //实现ICloneable接口的Clone方法
     8         public object Clone()
     9         {
    10             return this.MemberwiseClone();//浅拷贝
    11         }
    12     }
    13     class Department
    14     {
    15         public string DepartmentName { get; set; }
    16         public Department(string value)
    17         {
    18             DepartmentName = value;
    19         }
    20         public override string ToString()
    21         {
    22             return DepartmentName.ToString();
    23         }
    24     }

    调用浅拷贝代码:

     1      Employee emp1 = new Employee()
     2      {
     3          ID = "NO1",
     4          Age = 20,
     5          DepartmentName = new Department("Technology")
     6      };
     7      Employee emp2 = emp1.Clone() as Employee;//浅拷贝
     8 
     9      Console.WriteLine("-------初始化赋值------");
    10      Console.WriteLine(string.Format("[emp1] id:{0}	age:{1}	department:{2}", emp1.ID, emp1.Age, emp1.DepartmentName));
    11      Console.WriteLine(string.Format("[emp2] id:{0}	age:{1}	department:{2}", emp2.ID, emp2.Age, emp2.DepartmentName));
    12 
    13      Console.WriteLine("
    -------改变emp1的值-------");
    14      emp1.ID = "NO2";
    15      emp1.Age = 22;
    16      emp1.DepartmentName.DepartmentName = "sales";
    17      Console.WriteLine(string.Format("[emp1] id:{0}	age:{1}	department:{2}", emp1.ID, emp1.Age, emp1.DepartmentName));
    18      Console.WriteLine(string.Format("[emp2] id:{0}	age:{1}	department:{2}", emp2.ID, emp2.Age, emp2.DepartmentName));
    19 
    20      Console.WriteLine("
    -------改变emp2的值-------");
    21      emp2.ID = "NO3";
    22      emp2.Age = 24;
    23      emp2.DepartmentName.DepartmentName = "personnel";
    24      Console.WriteLine(string.Format("[emp1] id:{0}	age:{1}	department:{2}", emp1.ID, emp1.Age, emp1.DepartmentName));
    25      Console.WriteLine(string.Format("[emp2] id:{0}	age:{1}	department:{2}", emp2.ID, emp2.Age, emp2.DepartmentName));

    运行结果:

    -------初始化赋值------
    [emp1] id:NO1   age:20  department:Technology
    [emp2] id:NO1   age:20  department:Technology
    
    -------改变emp1的值-------
    [emp1] id:NO2   age:22  department:sales
    [emp2] id:NO1   age:20  department:sales
    
    -------改变emp2的值-------
    [emp1] id:NO2   age:22  department:personnel
    [emp2] id:NO3   age:24  department:personnel

    从结果可以看出,Age是值类型,ID是string类型这里被当做值类型处理,所以ID和Age修改了对另一个对象没有影响;Department属性是引用类型,浅拷贝emp1和emp2引用的是同一个Department对象,其中一个修改了DepartmentName的值会影响另一个。

    2、深拷贝:把源对象的值类型字段和引用类型字段,重新创建并赋值。在源对象(副本)中,修改值类型字段的值或者引用类型字段的值都不会影响到副本(源对象)。

    建议使用序列化的形式来进行深拷贝

    深拷贝代码:

        [Serializable]//标记可序列化
        class Employee : ICloneable
        {
            public string ID { get; set; }
            public int Age { get; set; }
            public Department DepartmentName { get; set; }
    
            //实现ICloneable接口的Clone方法
            public object Clone()
            {
                using (MemoryStream stream = new MemoryStream())
                {
                    BinaryFormatter formatter = new BinaryFormatter();
                    formatter.Serialize(stream, this);
                    stream.Seek(0, SeekOrigin.Begin);
                    return formatter.Deserialize(stream) as Employee;
                }
            }
        }
    
        [Serializable]//标记可序列化
        class Department
        {
            public string DepartmentName { get; set; }
            public Department(string value)
            {
                DepartmentName = value;
            }
            public override string ToString()
            {
                return DepartmentName.ToString();
            }
        }

    跟上面调用浅拷贝代码一样调用深拷贝,运行结果:

    -------初始化赋值------
    [emp1] id:NO1   age:20  department:Technology
    [emp2] id:NO1   age:20  department:Technology
    
    -------改变emp1的值-------
    [emp1] id:NO2   age:22  department:sales
    [emp2] id:NO1   age:20  department:Technology
    
    -------改变emp2的值-------
    [emp1] id:NO2   age:22  department:sales
    [emp2] id:NO3   age:24  department:personnel

    拷贝以后,无论是修改值类型还是引用类型,都对另一个对象没有影响。

    3、要同时实现深拷贝和浅拷贝,可以在Clone方法外,额外实现两个方法,声明为DeepClone和Shallow:

     1     [Serializable]//标记可序列化
     2     class Employee : ICloneable
     3     {
     4         public string ID { get; set; }
     5         public int Age { get; set; }
     6         public Department DepartmentName { get; set; }
     7 
     8         //实现ICloneable接口的Clone方法
     9         public object Clone()
    10         {
    11             return this.MemberwiseClone();
    12         }
    13 
    14         //深拷贝
    15         public Employee DeepClone()
    16         {
    17             using (MemoryStream stream = new MemoryStream())
    18             {
    19                 BinaryFormatter formatter = new BinaryFormatter();
    20                 formatter.Serialize(stream, this);
    21                 stream.Seek(0, SeekOrigin.Begin);
    22                 return formatter.Deserialize(stream) as Employee;
    23             }
    24         }
    25 
    26         //浅拷贝
    27         public Employee Shallow()
    28         {
    29             return this.Clone() as Employee;
    30         }
    31     }

    参考:《编写高质量代码改善C#程序的157个建议》陆敏技

  • 相关阅读:
    Mac下使用charles遇到的问题以及解决办法
    webp图片实践之路
    一个粗心的Bug,JSON格式不规范导致AJAX错误
    IE6/7下空div占用空间的问题
    通俗易懂的来讲讲DOM
    Javascript
    简单入门canvas
    HTML5 Boilerplate
    网页字体知识
    备战CKA每日一题——第8天 | initContainer概念、用法、使用场景简介;k8s secret env、volume考题引出
  • 原文地址:https://www.cnblogs.com/xuyouyou/p/13172411.html
Copyright © 2011-2022 走看看