• 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个建议》陆敏技

  • 相关阅读:
    HDU 4565 So Easy!(公式化简+矩阵)
    CentOS 64位下安装Postfix+Dovecot 配置邮件server笔记
    TreeView 高速单击时不运行AfterCheck时间
    小强的HTML5移动开发之路(19)——HTML5 Local Storage(本地存储)
    小强的HTML5移动开发之路(18)——HTML5地理定位
    小强的HTML5移动开发之路(17)——HTML5内联SVG
    小强的HTML5移动开发之路(16)——神奇的拖放功能
    小强的HTML5移动开发之路(15)——HTML5中的音频
    小强的HTML5移动开发之路(14)——Video标签详解
    小强的HTML5移动开发之路(13)——HTML5中的全局属性
  • 原文地址:https://www.cnblogs.com/xuyouyou/p/13172411.html
Copyright © 2020-2023  润新知