C#淺拷貝和深拷貝實(shí)例解析
在有些時(shí)候,我們需要從數(shù)據(jù)庫(kù)讀取數(shù)據(jù)填充對(duì)象或從硬盤(pán)讀取文件填充對(duì)象,但是這樣做相對(duì)耗時(shí)。這時(shí)候我們就想到了對(duì)象的拷貝。本文即以實(shí)例形式解析了C#淺拷貝和深拷貝的用法。具體如下:
一、淺拷貝
1.什么是"淺拷貝":
當(dāng)針對(duì)一個(gè)對(duì)象前拷貝的時(shí)候,對(duì)于對(duì)象的值類(lèi)型成員,會(huì)復(fù)制其本身,對(duì)于對(duì)象的引用類(lèi)型成員,僅僅復(fù)制對(duì)象引用,這個(gè)引用指向托管堆上的對(duì)象實(shí)例。
2.有一個(gè)對(duì)象,包含引用類(lèi)型的類(lèi)成員和值類(lèi)型的struct成員
Cinema包含引用類(lèi)型成員Room和值類(lèi)型成員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(); //對(duì)引用類(lèi)型實(shí)施淺復(fù)制 } }
3.測(cè)試拷貝后的效果
①打印出原先對(duì)象拷貝前值類(lèi)型和引用類(lèi)型成員的值
②對(duì)原先對(duì)象拷貝,打印出復(fù)制對(duì)象值類(lèi)型和引用類(lèi)型成員的值
③改變?cè)葘?duì)象的值,再次打印原先對(duì)象的值類(lèi)型和引用類(lèi)型成員的值
④再次打印復(fù)制對(duì)象值類(lèi)型和引用類(lèi)型成員的值
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("拷貝之前,結(jié)構(gòu)成員的字段值為{0},引用類(lèi)型成員的字段值為{1}", cinema1._film._name,cinema1._room._maxSeat); Console.WriteLine("拷貝之后,新的結(jié)構(gòu)成員的字段值為{0},引用類(lèi)型成員的字段值為{1}", cinema2._film._name, cinema2._room._maxSeat); //修改拷貝之前引用類(lèi)型的字段值 cinema1._film._name = "極品飛車(chē)"; cinema1._room._maxSeat = 80; Console.WriteLine("修改之后,結(jié)構(gòu)成員的字段值為{0},引用類(lèi)型成員的字段值為{1}", cinema1._film._name, cinema1._room._maxSeat); Console.WriteLine("修改之后,新的結(jié)構(gòu)成員的字段值為{0},引用類(lèi)型成員的字段值為{1}", cinema2._film._name, cinema2._room._maxSeat); Console.ReadKey(); }
運(yùn)行結(jié)果如下:
分析:
淺拷貝關(guān)鍵點(diǎn)是對(duì)引用類(lèi)型拷貝的是對(duì)象引用,這個(gè)引用指向托管堆上的對(duì)象實(shí)例。改變?cè)瓕?duì)應(yīng)引用類(lèi)型的值,會(huì)影響到復(fù)制對(duì)象。
二、深拷貝
1.什么是"深拷貝"
對(duì)引用成員指向的對(duì)象也進(jìn)行復(fù)制,在托管堆上賦值原先對(duì)象實(shí)例所包含的數(shù)據(jù),再在托管堆上創(chuàng)建新的對(duì)象實(shí)例。
2.通過(guò)對(duì)每個(gè)對(duì)象成員進(jìn)行復(fù)制進(jìn)行深拷貝
public object Clone() { Room room = new Room(); room._maxSeat = this._room._maxSeat;//復(fù)制當(dāng)前引用類(lèi)型成員的值到新對(duì)象 Film film = this._film; //值類(lèi)型直接賦值 Cinema cinema = new Cinema(room, film); return cinema; }
3.也可以通過(guò)序列化和反序列化進(jìn)行深拷貝
public object Clone1() { BinaryFormatter bf = new BinaryFormatter(); MemoryStream ms = new MemoryStream(); bf.Serialize(ms, this); //復(fù)制到流中 ms.Position = 0; return (bf.Deserialize(ms)); }
4.采用序列化和反序列化深拷貝,但必須把所有的類(lèi)打上[Serializable],測(cè)試代碼如下:
[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(); //對(duì)引用類(lèi)型實(shí)施淺復(fù)制 //} //深拷貝 對(duì)每個(gè)對(duì)象成員進(jìn)行復(fù)制 public object Clone() { Room room = new Room(); room._maxSeat = this._room._maxSeat;//復(fù)制當(dāng)前引用類(lèi)型成員的值到新對(duì)象 Film film = this._film; //值類(lèi)型直接賦值 Cinema cinema = new Cinema(room, film); return cinema; } //使用序列化和反序列化進(jìn)行復(fù)制 public object Clone1() { BinaryFormatter bf = new BinaryFormatter(); MemoryStream ms = new MemoryStream(); bf.Serialize(ms, this); //復(fù)制到流中 ms.Position = 0; return (bf.Deserialize(ms)); } }
5.測(cè)試拷貝后的效果
①打印出原先對(duì)象拷貝前值類(lèi)型和引用類(lèi)型成員的值
②對(duì)原先對(duì)象拷貝,打印出復(fù)制對(duì)象值類(lèi)型和引用類(lèi)型成員的值
③改變?cè)葘?duì)象的值,再次打印原先對(duì)象的值類(lèi)型和引用類(lèi)型成員的值
④再次打印復(fù)制對(duì)象值類(lèi)型和引用類(lèi)型成員的值
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("拷貝之前,結(jié)構(gòu)成員的字段值為{0},引用類(lèi)型成員的字段值為{1}", cinema1._film._name,cinema1._room._maxSeat); Console.WriteLine("拷貝之后,新的結(jié)構(gòu)成員的字段值為{0},引用類(lèi)型成員的字段值為{1}", cinema2._film._name, cinema2._room._maxSeat); //修改拷貝之前引用類(lèi)型的字段值 cinema1._film._name = "極品飛車(chē)"; cinema1._room._maxSeat = 80; Console.WriteLine("修改之后,結(jié)構(gòu)成員的字段值為{0},引用類(lèi)型成員的字段值為{1}", cinema1._film._name, cinema1._room._maxSeat); Console.WriteLine("修改之后,新的結(jié)構(gòu)成員的字段值為{0},引用類(lèi)型成員的字段值為{1}", cinema2._film._name, cinema2._room._maxSeat); Console.ReadKey(); }
結(jié)果:
分析:
深拷貝后,兩個(gè)對(duì)象的引用成員已經(jīng)分離,改變?cè)葘?duì)象引用類(lèi)型成員的值并不會(huì)對(duì)復(fù)制對(duì)象的引用類(lèi)型成員值造成影響。
相關(guān)文章
c#中string的特性介紹及注意事項(xiàng)小結(jié)
這篇文章主要給大家介紹了關(guān)于c#中string的特性介紹及注意事項(xiàng)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用c#具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11C#使用Excel動(dòng)態(tài)函數(shù)實(shí)現(xiàn)生成依賴(lài)列表
這篇文章主要為大家詳細(xì)介紹了如何在C#中使用?Excel?動(dòng)態(tài)函數(shù)生成依賴(lài)列表,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-02-02C#使用semaphore來(lái)管理異步下載請(qǐng)求的方法
這篇文章主要介紹了C#使用semaphore來(lái)管理異步下載請(qǐng)求的方法,涉及C#使用semaphore實(shí)現(xiàn)多線程管理的技巧,需要的朋友可以參考下2015-06-06HTML文本框的值改變后觸發(fā)后臺(tái)代碼的方法
asp.net用日期插件,當(dāng)選中一個(gè)日期時(shí)觸發(fā)一個(gè)事件,以查詢當(dāng)前日期的數(shù)據(jù)。這是要跟數(shù)據(jù)庫(kù)交互的。先貼出控件代碼:2013-04-04Visual C#類(lèi)的定義及實(shí)現(xiàn)方法實(shí)例解析
這篇文章主要介紹了Visual C#類(lèi)的定義及實(shí)現(xiàn)方法實(shí)例解析,對(duì)于新手來(lái)說(shuō)有不錯(cuò)的借鑒學(xué)習(xí)價(jià)值,需要的朋友可以參考下2014-07-07C#實(shí)現(xiàn)應(yīng)用程序的監(jiān)控與調(diào)試的示例代碼
日志記錄是軟件開(kāi)發(fā)中不可或缺的功能,它能幫助開(kāi)發(fā)者在應(yīng)用程序運(yùn)行時(shí)記錄重要信息,本文就來(lái)介紹一下常用日志記錄功能以及常用的日志庫(kù),感興趣的可以了解一下2024-03-03