最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

c#基礎(chǔ)系列之值類(lèi)型和引用類(lèi)型的深入理解

 更新時(shí)間:2018年09月16日 11:29:42   作者:大菜  
這篇文章主要給大家介紹了關(guān)于c#基礎(chǔ)系列之值類(lèi)型和引用類(lèi)型的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

前言

不知不覺(jué)已經(jīng)踏入坑已10余年之多,對(duì)于c#多多少少有一點(diǎn)自己的認(rèn)識(shí),寫(xiě)出來(lái)渴求同類(lèi)抨擊,對(duì)自己也算是個(gè)十年之癢的一個(gè)總結(jié)。

C#把數(shù)據(jù)類(lèi)型分為值類(lèi)型和引用類(lèi)型

1.1:從概念上來(lái)看,其區(qū)別是值類(lèi)型直接存儲(chǔ)值,而引用類(lèi)型存儲(chǔ)對(duì)值的引用。

1.2:這兩種類(lèi)型在內(nèi)存的不同地方,值類(lèi)型存儲(chǔ)在堆棧中,而引用類(lèi)型存儲(chǔ)在托管對(duì)上。存儲(chǔ)位置的不同會(huì)有不同的影響。

下面話(huà)不多說(shuō)了,來(lái)一起看看詳細(xì)的介紹吧

基本概念

CLR支持兩種類(lèi)型:值類(lèi)型和引用類(lèi)型。 面試過(guò)很多5年左右的同學(xué),有很多連值類(lèi)型和引用類(lèi)型的基本概念都回答不上來(lái),難道現(xiàn)在的c#開(kāi)發(fā)人員基礎(chǔ)這么弱了嗎?還是大家都不重視基礎(chǔ)呢?這個(gè)隨便找一篇博客都可以基礎(chǔ)入門(mén)的。

引用類(lèi)型

哪些類(lèi)型是引用類(lèi)型呢?其實(shí)一個(gè)可以稱(chēng)為”類(lèi)“的類(lèi)型都是引用類(lèi)型。 引用類(lèi)型總是從托管堆上分配的,常用的語(yǔ)法就是New XX(). C#的new 操作符會(huì)返回對(duì)象的指針 - 也就是指向?qū)ο髷?shù)據(jù)的內(nèi)存地址的一個(gè)引用。引用類(lèi)型的傳遞其實(shí)傳遞的是對(duì)象的指針(string類(lèi)型比較特殊),所以在特定的場(chǎng)景下性能是高于值類(lèi)型的。一個(gè)引用類(lèi)型在創(chuàng)建時(shí)默認(rèn)為null,也就是說(shuō)當(dāng)前變量不指向一個(gè)有效的對(duì)象,也就是我們常遇到的異?!拔磳?duì)象引用設(shè)置到對(duì)象的實(shí)例”。

值類(lèi)型

因?yàn)橐妙?lèi)型變量都需要進(jìn)行一次堆內(nèi)存的分配,這會(huì)給GC造成很大的壓力,所以CLR提供了輕量級(jí)類(lèi)型“值類(lèi)型”。 值類(lèi)型一般在線(xiàn)程棧上分配。(注意:值類(lèi)型可以嵌入一個(gè)引用對(duì)象中)一個(gè)值類(lèi)型變量其實(shí)就包含了值類(lèi)型實(shí)例的值,所以它沒(méi)有引用類(lèi)型的指針(大家猜想值類(lèi)型需不需要類(lèi)型對(duì)象指針呢?)

相同點(diǎn)和不同點(diǎn)

相同點(diǎn)

  • 值類(lèi)型和引用類(lèi)型都是System.Object的子類(lèi)
  • 值類(lèi)型和引用類(lèi)型都可以繼承接口。(很多人都認(rèn)為值類(lèi)型不能繼承接口)
 interface Itest
 {
 void test();
 }
 struct TestStruct : Itest
 {
 public void test()
 {
 throw new NotImplementedException();
 }
 }

不同點(diǎn)

  • 值類(lèi)型分配在堆棧上,引用類(lèi)型是在托管堆上分配的。這里需要指出一點(diǎn):如果一個(gè)引用類(lèi)型中的某個(gè)屬性是值類(lèi)型,這個(gè)值類(lèi)型的屬性是分配在托管堆上的。
  • 所有的值類(lèi)型都是隱式密封的(sealed),例如 :你不可能繼承int 來(lái)構(gòu)造自己的類(lèi)型。
  • 值類(lèi)型的每一次賦值都會(huì)執(zhí)行一次逐字段的復(fù)制,所以如果是頻繁賦值也會(huì)造成性能上的壓力,引用類(lèi)型的賦值只是指針的傳遞,其實(shí)也是生成新的指針實(shí)例。
  • 引用類(lèi)型額外有類(lèi)型對(duì)象指針和同步塊索引,值類(lèi)型是沒(méi)有的。所以我們平時(shí)使用lock 鎖的對(duì)象不可能是值類(lèi)型,因?yàn)橹殿?lèi)型沒(méi)有同步塊索引

性能

有的同學(xué)說(shuō)值類(lèi)型的性能高于引用類(lèi)型,那為什么不都用值類(lèi)型呢?引用類(lèi)型也是如此。任何東西都有兩面性,只有合適的類(lèi)型,沒(méi)有萬(wàn)能的類(lèi)型。

1、值類(lèi)型:所謂的.net Framework中的“輕量類(lèi)型”,為什么說(shuō)是“輕量”呢,這和他的內(nèi)存分配有直接關(guān)系,因?yàn)橹殿?lèi)型是分配在棧上,所以在GC的控制之外,不會(huì)對(duì)GC造成壓力。那是不是可以隨便用呢?當(dāng)然不是,舉個(gè)例子:我自定義一個(gè)struct 類(lèi)型作為一個(gè)方法的參數(shù)會(huì)發(fā)生什么呢?每次調(diào)用都會(huì)發(fā)生全字段的賦值,這是不可接受的,這也是典型的值類(lèi)型勿用場(chǎng)景。

2、引用類(lèi)型:引用類(lèi)型分配在堆中,所以會(huì)影響GC,如果頻繁的初始化引用類(lèi)型,對(duì)GC的壓力是很大的,因?yàn)槊恳淮畏峙涠加锌赡軙?huì)強(qiáng)制執(zhí)行一次垃圾收集操作。另外提一點(diǎn),引用類(lèi)型的所占內(nèi)存,并非所有屬性/字段的和,堆上分配的每個(gè)對(duì)象都有一些額外的成員,這些成員必須初始化。(類(lèi)型對(duì)象指針和內(nèi)存塊索引)。

3、裝箱拆箱:所謂裝箱就是將值類(lèi)型轉(zhuǎn)化為引用類(lèi)型的過(guò)程。拆箱則相反(只是概念上相反,實(shí)際編譯器的操作不一樣)。有的同學(xué)說(shuō)裝箱拆箱影響性能,那到底是裝箱影響呢還是拆箱呢還是都影響呢?

裝箱發(fā)生了什么過(guò)程呢:

  • 在托管堆中分配好內(nèi)存,分配的內(nèi)存量是值類(lèi)型的各個(gè)字段需要的內(nèi)存量加上托管堆上所以對(duì)象的兩個(gè)額外成員(類(lèi)型對(duì)象指針,同步塊索引)需要的內(nèi)存量
  • 值類(lèi)型的字段復(fù)制到新分配的堆內(nèi)存中
  • 返回對(duì)象的地址,這個(gè)地址就是這個(gè)對(duì)象的引用

拆箱發(fā)生了什么過(guò)程呢:

  • 獲取已經(jīng)裝箱的值類(lèi)型實(shí)例的指針
  • 把獲取到的值復(fù)制到棧

所以裝箱是比較耗費(fèi)性能的,還有可能引發(fā)一次GC操作,而拆箱只是一個(gè)獲取指針的過(guò)程耗費(fèi)資源要比裝箱小的多。注意:一個(gè)對(duì)象拆箱之后只能還原為原先未裝箱之前的類(lèi)型,例如:你不能把int32類(lèi)型裝箱后還原為int16類(lèi)型。 所以面試的時(shí)候可以和面試官裝B一下了~~

測(cè)試?yán)?/strong>

值類(lèi)型引用類(lèi)型分別初始化N次消耗的時(shí)間,代碼如下

static void Main(string[] args)
 {
 Console.WriteLine("test start");
 int totalCount = 10000000;
 Stopwatch sw = new Stopwatch();
 sw.Start();
 for (int i = 0; i < totalCount; i++)
 {
  TestRef temp = new TestRef() { Id = i, Name = "test" };
 }
 sw.Stop();
 Console.WriteLine($"引用類(lèi)型耗時(shí):{sw.ElapsedMilliseconds}");
 sw.Reset();
 sw.Start();

 for (int i = 0; i < totalCount; i++)
 {
  TestVal temp = new TestVal() { Id = i, Name = "test" };
 }
 sw.Stop();
 Console.WriteLine($"值類(lèi)型耗時(shí):{sw.ElapsedMilliseconds}");
 Console.Read();
 }

 class TestRef
 {
 public int Id { get; set; }
 public string Name { get; set; }
 }
 struct TestVal
 {
 public int Id { get; set; }
 public string Name { get; set; }
 }

運(yùn)行結(jié)果:

引用類(lèi)型耗時(shí):205
值類(lèi)型耗時(shí):152

可見(jiàn)初始化速度值類(lèi)型是優(yōu)于引用類(lèi)型的,也可能是引用類(lèi)型引發(fā)了GC導(dǎo)致。

作為方法參數(shù)傳遞,代碼如下:

static void Main(string[] args)
 {
 Console.WriteLine("test start");
 long totalCount = 1000000000;
 Stopwatch sw = new Stopwatch();
 sw.Start();

 TestRef tempRef = new TestRef() { Id = 1, Name = "test" , Name2="r3rewfdsafdsa", Name3="fsrewfdsafdsafdsa", Name4="fdafdasfdsafdsa", Name5="432tretsfds", Name6="fdsafdasfdasfd" };
 for (int i = 0; i < totalCount; i++)
 {
  TestR(tempRef);
 }
 sw.Stop();
 Console.WriteLine($"引用類(lèi)型耗時(shí):{sw.ElapsedMilliseconds}");
 sw.Reset();
 sw.Start();
 TestVal tempVal = new TestVal() { Id = 1, Name = "test", Name2 = "r3rewfdsafdsa", Name3 = "fsrewfdsafdsafdsa", Name4 = "fdafdasfdsafdsa", Name5 = "432tretsfds", Name6 = "fdsafdasfdasfd" };
 for (int i = 0; i < totalCount; i++)
 {
  TestV(tempVal);
 }
 sw.Stop();
 Console.WriteLine($"值類(lèi)型耗時(shí):{sw.ElapsedMilliseconds}");
 Console.Read();
 }
 static void TestR(TestRef r)
 {
 return;
 }
 static void TestV(TestVal v)
 {
 return;
 }

 class TestRef
 {
 public int Id { get; set; }
 public string Name { get; set; }
 public string Name2 { get; set; }
 public string Name3 { get; set; }
 public string Name4 { get; set; }
 public string Name5 { get; set; }
 public string Name6 { get; set; }
 }
 struct TestVal
 {
 public int Id { get; set; }
 public string Name { get; set; }
 public string Name2 { get; set; }
 public string Name3 { get; set; }
 public string Name4 { get; set; }
 public string Name5 { get; set; }
 public string Name6 { get; set; }
 }

運(yùn)行結(jié)果:

引用類(lèi)型耗時(shí):4437
值類(lèi)型耗時(shí):5226

可見(jiàn)在普通情況下,作為參數(shù)值類(lèi)型和引用類(lèi)型用時(shí)差距不大,但是,如果值類(lèi)型的實(shí)例屬性比較多的情況下差距降進(jìn)一步拉大。

非正式環(huán)境測(cè)試用例,結(jié)果僅供參考

應(yīng)用場(chǎng)景

不止是面試的時(shí)候經(jīng)常問(wèn)應(yīng)用場(chǎng)景這個(gè)問(wèn)題,就是自己平時(shí)寫(xiě)程序也應(yīng)該清楚。程序設(shè)計(jì)選擇的時(shí)候大部分場(chǎng)景都是用引用類(lèi)型,但是如果你滿(mǎn)足下列條件,值類(lèi)型可能更適用:

  • 類(lèi)型不會(huì)派生出任何其它類(lèi)型,也就是說(shuō)不會(huì)有被繼承的可能
  • 類(lèi)型不需要繼承其他類(lèi)型
  • 類(lèi)型的實(shí)例比較小,并且不會(huì)被作為方法參數(shù),不會(huì)被頻繁賦值
  • 你永遠(yuǎn)不會(huì)用到類(lèi)型釋放時(shí)候的通知,因?yàn)橐妙?lèi)型利用析構(gòu)函數(shù)可以利用其他手段可以得到釋放時(shí)候的通知。
  • 如果你的類(lèi)型實(shí)例不會(huì)發(fā)生值的改變或者可以認(rèn)為是readonly性質(zhì)的,值類(lèi)型或許是首選。

其他

  • 所有的值類(lèi)型都從System.ValueType 派生,System.ValueType繼承System.Object,但是System.ValueType 重寫(xiě)了Equals 和GetHashCode 方法,其實(shí)在這里才是真正和引用類(lèi)型的分割線(xiàn)。
  • 因?yàn)橹殿?lèi)型有裝箱拆箱的操作,所以像ArrayList這樣的集合性能是非常令人擔(dān)憂(yōu)的。所以c# 2.0 出現(xiàn)了泛型 例如:List .....來(lái)保證了類(lèi)型安全,同時(shí)又避免了拆箱裝箱,因?yàn)椴皇俏叶x的類(lèi)型 ,你TMD根本連編譯器那一關(guān)都過(guò)不了 哈哈哈~~~~

順便說(shuō)一句,好久不寫(xiě)博客,樣式真實(shí)花時(shí)間啊,后來(lái)干脆寫(xiě)markdown格式的,請(qǐng)大家見(jiàn)諒??!

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • C#可空類(lèi)型用法分析

    C#可空類(lèi)型用法分析

    這篇文章主要介紹了C#可空類(lèi)型用法,實(shí)例分析了C#可空類(lèi)型的功能、定義及使用方法,需要的朋友可以參考下
    2015-05-05
  • C#中使用反射遍歷一個(gè)對(duì)象屬性及值的小技巧

    C#中使用反射遍歷一個(gè)對(duì)象屬性及值的小技巧

    這篇文章主要介紹了C#中使用反射遍歷一個(gè)對(duì)象屬性及值的小技巧,這在很時(shí)候應(yīng)該都非常有用,本文直接給出實(shí)例代碼,需要的朋友可以參考下
    2015-07-07
  • C#操作RabbitMQ的完整實(shí)例

    C#操作RabbitMQ的完整實(shí)例

    這篇文章主要為大家詳細(xì)介紹了C#操作RabbitMQ的完整實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • C#使用 Salt + Hash 來(lái)為密碼加密

    C#使用 Salt + Hash 來(lái)為密碼加密

    本文主要介紹了幾種常見(jiàn)的破解密碼的方法,為密碼加鹽(Salt)以及在.NET中的實(shí)現(xiàn)等。具有一定的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-01-01
  • C#刪除文件目錄或文件的解決方法

    C#刪除文件目錄或文件的解決方法

    本篇文章是對(duì)C#中如何刪除文件目錄或文件的解決方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • C#實(shí)現(xiàn)讀取匿名對(duì)象屬性值的方法示例總結(jié)

    C#實(shí)現(xiàn)讀取匿名對(duì)象屬性值的方法示例總結(jié)

    這篇文章主要介紹了C#實(shí)現(xiàn)讀取匿名對(duì)象屬性值的方法,結(jié)合實(shí)例形式總結(jié)分析了C#通過(guò)反射、轉(zhuǎn)換等方法讀取匿名對(duì)象屬性值的相關(guān)操作技巧,需要的朋友可以參考下
    2020-03-03
  • c# 曲線(xiàn)圖生成代碼

    c# 曲線(xiàn)圖生成代碼

    c# 曲線(xiàn)圖生成代碼,需要的朋友可以參考下。
    2011-07-07
  • 使用C#在Word文檔中插入表格

    使用C#在Word文檔中插入表格

    在許多企業(yè)應(yīng)用場(chǎng)景中,Word 文檔依舊是最常用的信息呈現(xiàn)與內(nèi)容輸出格式,批量生成合同、輸出數(shù)據(jù)報(bào)表、構(gòu)建結(jié)構(gòu)化文檔時(shí),表格往往是不可或缺的組成部分,本文將介紹在 C# 中如何以編程方式創(chuàng)建 Word 文檔、插入表格、設(shè)置樣式,需要的朋友可以參考下
    2025-11-11
  • 基于C#的電視臺(tái)節(jié)目表接口調(diào)用代碼

    基于C#的電視臺(tái)節(jié)目表接口調(diào)用代碼

    這篇文章主要介紹了基于C#的電視臺(tái)節(jié)目表接口調(diào)用代碼的相關(guān)資料,需要的朋友可以參考下
    2016-06-06
  • 如何使用C#掃描并讀取圖片中的文字

    如何使用C#掃描并讀取圖片中的文字

    本文介紹如何通過(guò)C# 程序來(lái)掃描并讀取圖片中的文字,這里以創(chuàng)建一個(gè).Net Core程序?yàn)槔?。下面是具體步驟,供參考。有此需求的朋友可以了解下
    2021-06-06

最新評(píng)論

石嘴山市| 弥勒县| 天镇县| 通城县| 新泰市| 克山县| 化德县| 达尔| 浦江县| 永川市| 永顺县| 定结县| 商洛市| 紫金县| 昭平县| 贵定县| 吉水县| 胶南市| 宁津县| 元江| 松溪县| 太康县| 江津市| 肇州县| 尼木县| 定陶县| 工布江达县| 茶陵县| 竹北市| 阳高县| 庆云县| 东山县| 武邑县| 庐江县| 河北省| 馆陶县| 南和县| 巨鹿县| 金坛市| 禹州市| 湘潭市|