zoukankan      html  css  js  c++  java
  • C# 浅拷贝与深拷贝(复制)

          在有些时候,我们需要从数据库读取数据填充对象或从硬盘读取文件填充对象,但是这样做相对耗时。这时候我们就想到了对象的拷贝。本文即以实例形式解析了C#浅拷贝和深拷贝的用法。

           C#中有两种类型变量,一种 是值类型变量,一种是引用类型变量。对于前者,copy是属于全盘复制;而对后者,一般的copy只是浅copy,相当于只传递一个引用指针一样。因此 对于后者进行真正copy的时候,也是最费事的,具体的说,必须为其实现ICloneable接口中提供的Clone方法。

    一、浅拷贝

    1.什么是"浅拷贝":

    当针对一个对象浅拷贝的时候,对于对象的值类型成员,会复制其本身,对于对象的引用类型成员,仅仅复制对象引用,这个引用指向托管堆上的对象实例。

    例如:有一个对象,包含引用类型的类成员和值类型的struct成员

      即:Cinema包含 引用类型成员Room和值类型成员Film。

    public class Room
    {
      public int _maxSeat;
     
      public Room(int maxSeat)
      {
        this._maxSeat = maxSeat;
      }
    }
     
    public struct Film
    {
      public string _name;
     
      public Film(string name)
      {
        this._name = name;
      }
    }
     
    public class Cinema
    {
      public Room _room;
      public Film _film;
     
      public Cinema(Room room, Film film)
      {
        this._room = room;
        this._film = film;
      }
     
      public object Clone()
      {
        return MemberwiseClone(); //对引用类型实施浅复制
      }
    }
    View Code

    2.测试拷贝后的效果

    ①打印出原先对象  拷贝前值类型和引用类型成员的值 
    ②对原先对象拷贝,打印出复制对象值类型和引用类型成员的值 
    ③改变原先对象的值,再次打印原先对象的值类型和引用类型成员的值 
    ④再次打印复制对象值类型和引用类型成员的值

    static void Main(string[] args)
    {
      Room room1 = new Room(60);
      Film film1 = new Film("家园防线");
      Cinema cinema1 = new Cinema(room1, film1);
      Cinema cinema2 = (Cinema)cinema1.Clone();
      Console.WriteLine("拷贝之前,结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema1._film._name,cinema1._room._maxSeat);
     
      Console.WriteLine("拷贝之后,新的结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema2._film._name, cinema2._room._maxSeat);
     
      //修改拷贝之前引用类型的字段值
      cinema1._film._name = "极品飞车";
      cinema1._room._maxSeat = 80;
     
      Console.WriteLine("修改之后,结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema1._film._name, cinema1._room._maxSeat);
      Console.WriteLine("修改之后,新的结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema2._film._name, cinema2._room._maxSeat);
     
      Console.ReadKey();
    }
    View Code

    运行结果如下:

    分析:

    浅拷贝关键点是对引用类型拷贝的是对象引用,这个引用指向托管堆上的对象实例。改变原对应引用类型的值,会影响到复制对象。

    二、深拷贝

    1.什么是"深拷贝"

    对引用成员指向的对象也进行复制,在托管堆上赋值原先对象实例所包含的数据,再在托管堆上创建新的对象实例。

    2.通过对每个对象成员进行复制进行深拷贝

    public object Clone()
    {
      Room room = new Room();
      room._maxSeat = this._room._maxSeat;//复制当前引用类型成员的值到新对象 </span>
      Film film = this._film; //值类型直接赋值
      Cinema cinema = new Cinema(room, film);
      return cinema;
    }

    3.也可以通过序列化和反序列化进行深拷贝

    public object Clone1()
    {
      BinaryFormatter bf = new BinaryFormatter();
      MemoryStream ms = new MemoryStream();
      bf.Serialize(ms, this); //复制到流中
      ms.Position = 0;
      return (bf.Deserialize(ms));
    }

    4.采用序列化和反序列化深拷贝,但必须把所有的类打上[Serializable],测试代码如下:

    [Serializable]
    public class Room
    {
      public int _maxSeat;
     
      public Room()
      {}
     
      public Room(int maxSeat)
      {
        this._maxSeat = maxSeat;
      }
    }
     
    [Serializable]
    public struct Film
    {
      public string _name;
     
      public Film(string name)
      {
        this._name = name;
      }
    }
     
    [Serializable]
    public class Cinema
    {
      public Room _room;
      public Film _film;
     
      public Cinema(Room room, Film film)
      {
        this._room = room;
        this._film = film;
      }
     
      //浅拷贝
      //public object Clone()
      //{
      //  return MemberwiseClone(); //对引用类型实施浅复制
      //}
     
      //深拷贝 对每个对象成员进行复制
      public object Clone()
      {
        Room room = new Room();
        room._maxSeat = this._room._maxSeat;//复制当前引用类型成员的值到新对象 
        Film film = this._film; //值类型直接赋值
        Cinema cinema = new Cinema(room, film);
        return cinema;
      }
     
      //使用序列化和反序列化进行复制
      public object Clone1()
      {
        BinaryFormatter bf = new BinaryFormatter();
        MemoryStream ms = new MemoryStream();
        bf.Serialize(ms, this); //复制到流中
        ms.Position = 0;
        return (bf.Deserialize(ms));
      }
    }
    View Code
    5.测试拷贝后的效果
    static void Main(string[] args)
       {
         Room room1 = new Room(60);
         Film film1 = new Film("家园防线");
         Cinema cinema1 = new Cinema(room1, film1);
         Cinema cinema2 = (Cinema)cinema1.Clone1();
         Console.WriteLine("拷贝之前,结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema1._film._name,cinema1._room._maxSeat);
     
         Console.WriteLine("拷贝之后,新的结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema2._film._name, cinema2._room._maxSeat);
     
         //修改拷贝之前引用类型的字段值
         cinema1._film._name = "极品飞车";
         cinema1._room._maxSeat = 80;
     
         Console.WriteLine("修改之后,结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema1._film._name, cinema1._room._maxSeat);
         Console.WriteLine("修改之后,新的结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema2._film._name, cinema2._room._maxSeat);
     
         Console.ReadKey();
       }
    View Code
    结果:
    分析:
    深拷贝后,两个对象的引用成员已经分离,改变原先对象引用类型成员的值
    并不会对复制对象的引用类型成员值造成影响。



  • 相关阅读:
    FZU 2098 刻苦的小芳(卡特兰数,动态规划)
    卡特兰数总结
    FZU 1064 教授的测试(卡特兰数,递归)
    HDU 4745 Two Rabbits(区间DP,最长非连续回文子串)
    Java 第十一届 蓝桥杯 省模拟赛 正整数的摆动序列
    Java 第十一届 蓝桥杯 省模拟赛 反倍数
    Java 第十一届 蓝桥杯 省模拟赛 反倍数
    Java 第十一届 蓝桥杯 省模拟赛 反倍数
    Java 第十一届 蓝桥杯 省模拟赛 凯撒密码加密
    Java 第十一届 蓝桥杯 省模拟赛 凯撒密码加密
  • 原文地址:https://www.cnblogs.com/peterYong/p/6556628.html
Copyright © 2011-2022 走看看