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ì)象的值類型成員,會(huì)復(fù)制其本身,對(duì)于對(duì)象的引用類型成員,僅僅復(fù)制對(duì)象引用,這個(gè)引用指向托管堆上的對(duì)象實(shí)例。
2.有一個(gè)對(duì)象,包含引用類型的類成員和值類型的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(); //對(duì)引用類型實(shí)施淺復(fù)制
}
}
3.測(cè)試拷貝后的效果
①打印出原先對(duì)象拷貝前值類型和引用類型成員的值
②對(duì)原先對(duì)象拷貝,打印出復(fù)制對(duì)象值類型和引用類型成員的值
③改變?cè)葘?duì)象的值,再次打印原先對(duì)象的值類型和引用類型成員的值
④再次打印復(fù)制對(duì)象值類型和引用類型成員的值
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},引用類型成員的字段值為{1}", cinema1._film._name,cinema1._room._maxSeat);
Console.WriteLine("拷貝之后,新的結(jié)構(gòu)成員的字段值為{0},引用類型成員的字段值為{1}", cinema2._film._name, cinema2._room._maxSeat);
//修改拷貝之前引用類型的字段值
cinema1._film._name = "極品飛車(chē)";
cinema1._room._maxSeat = 80;
Console.WriteLine("修改之后,結(jié)構(gòu)成員的字段值為{0},引用類型成員的字段值為{1}", cinema1._film._name, cinema1._room._maxSeat);
Console.WriteLine("修改之后,新的結(jié)構(gòu)成員的字段值為{0},引用類型成員的字段值為{1}", cinema2._film._name, cinema2._room._maxSeat);
Console.ReadKey();
}
運(yùn)行結(jié)果如下:

分析:
淺拷貝關(guān)鍵點(diǎn)是對(duì)引用類型拷貝的是對(duì)象引用,這個(gè)引用指向托管堆上的對(duì)象實(shí)例。改變?cè)瓕?duì)應(yīng)引用類型的值,會(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)前引用類型成員的值到新對(duì)象
Film film = this._film; //值類型直接賦值
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.采用序列化和反序列化深拷貝,但必須把所有的類打上[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ì)引用類型實(shí)施淺復(fù)制
//}
//深拷貝 對(duì)每個(gè)對(duì)象成員進(jìn)行復(fù)制
public object Clone()
{
Room room = new Room();
room._maxSeat = this._room._maxSeat;//復(fù)制當(dāng)前引用類型成員的值到新對(duì)象
Film film = this._film; //值類型直接賦值
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ì)象拷貝前值類型和引用類型成員的值
②對(duì)原先對(duì)象拷貝,打印出復(fù)制對(duì)象值類型和引用類型成員的值
③改變?cè)葘?duì)象的值,再次打印原先對(duì)象的值類型和引用類型成員的值
④再次打印復(fù)制對(duì)象值類型和引用類型成員的值
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},引用類型成員的字段值為{1}", cinema1._film._name,cinema1._room._maxSeat);
Console.WriteLine("拷貝之后,新的結(jié)構(gòu)成員的字段值為{0},引用類型成員的字段值為{1}", cinema2._film._name, cinema2._room._maxSeat);
//修改拷貝之前引用類型的字段值
cinema1._film._name = "極品飛車(chē)";
cinema1._room._maxSeat = 80;
Console.WriteLine("修改之后,結(jié)構(gòu)成員的字段值為{0},引用類型成員的字段值為{1}", cinema1._film._name, cinema1._room._maxSeat);
Console.WriteLine("修改之后,新的結(jié)構(gòu)成員的字段值為{0},引用類型成員的字段值為{1}", cinema2._film._name, cinema2._room._maxSeat);
Console.ReadKey();
}
結(jié)果:
分析:
深拷貝后,兩個(gè)對(duì)象的引用成員已經(jīng)分離,改變?cè)葘?duì)象引用類型成員的值并不會(huì)對(duì)復(fù)制對(duì)象的引用類型成員值造成影響。
相關(guān)文章
C# 實(shí)現(xiàn)WebSocket服務(wù)端教程
這篇文章主要介紹了C# 實(shí)現(xiàn)WebSocket服務(wù)端教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10
C#實(shí)現(xiàn)表格數(shù)據(jù)轉(zhuǎn)實(shí)體的示例代碼
在實(shí)際開(kāi)發(fā)過(guò)程中,特別是接口對(duì)接之類的,對(duì)于這種需求是屢見(jiàn)不鮮,現(xiàn)在很多在線平臺(tái)也都提供了像json轉(zhuǎn)實(shí)體、sql轉(zhuǎn)實(shí)體等。本文將用C#實(shí)現(xiàn)這一功能,需要的可以參考一下2022-09-09
關(guān)于c#中枚舉類型支持顯示中文的擴(kuò)展說(shuō)明
需求?。骸∶杜e類型在界面顯示的時(shí)候可以顯示相應(yīng)的中文信息, 這樣界面對(duì)用戶友好 . 場(chǎng)景?。骸≡谝恍I(yè)務(wù)中涉及到審核功能的時(shí)候, 往往有這幾個(gè)狀態(tài)?。何此蛯?, 審核中 ,審核通過(guò), 駁回?。∵@個(gè)時(shí)候我們會(huì)定義一個(gè)枚舉類型來(lái)描述 :2013-03-03
C#實(shí)現(xiàn)解析百度天氣數(shù)據(jù),Rss解析百度新聞以及根據(jù)IP獲取所在城市的方法
這篇文章主要介紹了C#實(shí)現(xiàn)解析百度天氣數(shù)據(jù),Rss解析百度新聞以及根據(jù)IP獲取所在城市的方法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2014-10-10
C#實(shí)現(xiàn)Excel合并單元格數(shù)據(jù)導(dǎo)入數(shù)據(jù)集詳解
這篇文章主要為大家詳細(xì)介紹了C#如何實(shí)現(xiàn)Excel合并單元格數(shù)據(jù)導(dǎo)入數(shù)據(jù)集,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-01-01
C# 站點(diǎn)IP訪問(wèn)頻率限制 針對(duì)單個(gè)站點(diǎn)的實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇C# 站點(diǎn)IP訪問(wèn)頻率限制 針對(duì)單個(gè)站點(diǎn)的實(shí)現(xiàn)方法。小編覺(jué)的挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-12-12

