在有些時候,我們需要從資料庫讀取資料填充對象或從硬碟讀取檔案填充對象,但是這樣做相對耗時。這時候我們就想到了對象的拷貝。本文即以執行個體形式解析了C#淺拷貝和深拷貝的用法。具體如下:
一、淺拷貝
1.什麼是"淺拷貝":
當針對一個對象前拷貝的時候,對於對象的實值型別成員,會複製其本身,對於對象的參考型別成員,僅僅複製對象引用,這個引用指向託管堆上的對象執行個體。
2.有一個對象,包含參考型別的類成員和實值型別的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(); //對參考型別實施淺複製 }}
3.測試拷貝後的效果
①列印出原先對象拷貝前實值型別和參考型別成員的值
②對原先對象拷貝,列印出複製對象實值型別和參考型別成員的值
③改變原先對象的值,再次列印原先對象的實值型別和參考型別成員的值
④再次列印複製對象實值型別和參考型別成員的值
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();}
分析:
淺拷貝關鍵點是對參考型別拷貝的是對象引用,這個引用指向託管堆上的對象執行個體。改變原對應參考型別的值,會影響到複製對象。
二、深拷貝
1.什麼是"深拷貝"
對引用成員指向的對象也進行複製,在託管堆上賦值原先對象執行個體所包含的資料,再在託管堆上建立新的對象執行個體。
2.通過對每個對象成員進行複製進行深拷貝
public object Clone(){ Room room = new Room(); room._maxSeat = this._room._maxSeat;//複製當前參考型別成員的值到新對象 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)); }}
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(); }
結果:
分析:
深拷貝後,兩個對象的引用成員已經分離,改變原先對象參考型別成員的值並不會對複製對象的參考型別成員值造成影響。