日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

[读书笔记]C#学习笔记三: C#类型详解..

發布時間:2025/3/21 C# 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [读书笔记]C#学习笔记三: C#类型详解.. 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

這次分享的主要內容有五個, 分別是值類型和引用類型,?裝箱與拆箱,常量與變量,運算符重載,static字段和static構造函數. 后期的分享會針對于C#2.0 3.0 4.0 等新特性進行. 再會有三篇博客 ?這個系列的就會結束了. 也算是自己對園子中@Learning Hard出版的<<C#學習筆記>>的一個總結了. 博客內容基本上都是白天抽空在公司寫好的了, 但是由于公司內部網絡不能登錄博客園所以只能夠夜晚拿回來修改, ?寫的不好或者不對的地方也請各位大神指出. 在下感激不盡了.?

1,值類型和引用類型
1.1 值類型與引用類型簡介
C#值類型數據直接在他自身分配到的內存中存儲數據,而C#引用類型只是包含指向存儲數據位置的指針。

C#值類型,我們可以把他歸納成三類:
  第一類: 基礎數據類型(string類型除外):包括整型、浮點型、十進制型、布爾型。  
                     ?整型包括:sbyte、byte、char、short、ushort、int、uint、long、ulong這九種類型;
                    ? 浮點型就包括 float 和 double 兩種類型;
                   ? ? ?十進制型就是 decimal ;
                    ? 布爾型就是 bool 型了。
  第二類:結構類型:就是struct型
  第三類:枚舉類型:就是enum型
?C#引用類型有五種:class、interface、delegate、object、string、Array。
上面說的是怎么區分哪些C#值類型和C#引用類型,而使用上也是有區別的。所有值類型的數據都無法為null的(這里可空類型是可以為空的),聲明后必須賦以初值;引用類型才允許 為null。

1.2 值類型與引用類型的區別
值類型與引用類型的區別是面試中經常經常問到的問題,完美的回答當然不能只是簡單地重復兩者的概念,因為面試官更希望你答出他們之間深層的區別--不同的內存分布
值類型通常被分配到縣城的堆棧上,而引用類型則被分配到托管堆上。不同的分配位置導致了不用的管理機制,值類型的管理由操作系統負責,而引用類型的管理則由垃圾回收器(GC)負責。

?

1 class Program 2 { 3 static void Main() 4 { 5 //valueType是值類型 6 int valueType = 3; 7 //refType是引用類型 8 string regType = "abc"; 9 } 10 }

?

圖1:?

值類型和引用類型的區別在實際數據的存儲位置:值類型的變量和實際數據都存儲在堆棧中;
而引用類型則只有變量存儲在堆棧中,變量存儲實際數據的地址,實際數據存儲在與地址相
對應的托管堆中。

1.3引用類型中嵌套定義值類型
如果類的字段類型是值類型,它將作為引用類型實例的一部分,被分配到托管堆中。但那些作為局部變量
(例如下列代碼中的c變量)的值類型,則仍然會分配到線程堆棧中。

?

1 //引用類型嵌套值類型的情況 2 public class NestedValueTypeInRef 3 { 4 //valueType作為引用類型的一部分被分配到托管堆上 5 private int valueType = 3; 6 7 public void method() 8 { 9 //c被分配到線程堆棧上 10 char c = 'c'; 11 } 12 } 13 14 class Program 15 { 16 static void Main(string[] args) 17 { 18 NestedRefTypeInValue refType = new NestedRefTypeInValue(); 19 } 20 }

?

以上代碼的內存分配情況如圖2所示:

1.4 值類型中嵌套定義引用類型

1 public class TestClass 2 { 3 public int x; 4 public int y; 5 } 6 7 //值類型嵌套定義引用類型的情況 8 public struct NestedRefTypeValue 9 { 10 //結構體字段,注意,結構體字段不能被初始化 11 private TestClass classinValueType; 12 13 //結構體中的構造函數,注意,結構體中不能定義無參的構造函數 14 public NestedRefTypeInValue(TestClass t) 15 { 16 calssinValueType.x = 3; 17 calssinValueType.y = 5; 18 calssinValueType = t; 19 } 20 } 21 22 class Program 23 { 24 static void Main(string[] args) 25 { 26 //值類型變量 27 NestedRefTypeInValue valueType = new NestedRefTypeInValue(new TestClass()); 28 } 29 }

?

以上代碼的內存分配情況如圖3所示:

從以上分析可以得出結論:值類型實例中會被分配到它聲明的地方,聲明的是局部變量時,將被分配到棧上。而聲明為引用類型時,則被分配到托管堆上。
而引用類型實例中是被分配到托管堆上。

上面只是分析了值類型與引用類型的內存分布方面的區別, 除此之外,二者還存在其他幾個方面的區別,現總結如下:
1。值類型繼承自ValueType, ValueType又繼承自System.Object; 而引用類型則直接繼承于System.Object。
2。值類型的內存不受GC控制,作用域結束時,值類型會被操作系統自行釋放,從而減少托管堆的壓力;而引用類型的內存管理則由GC來完成。所以與引用類相比,只類型在性能上更具有優勢。
3。若只類型的密封的(sealed), 你將不能把只類型作為其他任何類型的基類;而引用類型則一般具有繼承性,這里指的是類和接口。
4。值類型不能為null值(非空類型占不討論),它會被默認初始化為數值0; 而引用類型在默認情況下會被初始化為null值,表示不指向托管堆中的任何地址。對值null的引用類型的任何操作,都會引發空指針異常。
5。由于值類型變量包含其實際數據,因此在默認情況下,只類型之間的參數傳遞不會印象變量本身; 而引用類型變量保存的是數據的引用地址,它們作為參數被傳遞時,參數會發生改變,從而影響應用類型變量的值。


2,兩大類型間的轉換--裝箱與拆箱
類型轉換主要分為以下幾種方式:
1, 隱式類型轉換:由低級別類型向高級別類型的轉換過程。例如:派生類可以隱式的轉換為它的父類,裝箱過程就輸入這種隱式類型轉換。
2, 顯示類型轉換:也叫做強制類型轉換,但是這種轉換可能會導致精度損失或者出現運行時異常。
3, 通過is和as運算符進行安全的類型轉換。
4, 通過.Net 類庫中的Convert類來完成類型轉換。

下面主要介紹只類型與引用類型間的一種轉換:裝箱和拆箱
裝箱:值類型轉換為引用類型的過程
拆箱:引用類型轉換為值類型的過程
裝箱過程中,系統會在托管堆中生成一份堆棧中值類型對象的副本。而拆箱則是從托管堆中將引用類型所指向的已裝箱數據復制回值類型對象的過程。

下面通過示例從內存的角度對兩個過程進行分入分析:

1 class Program 2 { 3 static void Main() 4 { 5 int i = 3; 6 //裝箱操作 7 object o = i; 8 //拆箱操作 9 int y = (int)o; 10 } 11 }


以上代碼分別執行了一次裝箱和拆箱操作。
裝箱操作可以具體分為以下3個步驟:
(1)內存分配: 在托管堆中分配好內存空間以存放復制的實際數據?
(2)完成實際數據復制:將值類型實例的實際數據復制到新分配的內存中
(3)地址返回: 將托管堆中的對象地址返回給引用類型變量。

裝箱過程就是通過這3步完成的,如圖4所示。


在IL代碼中,裝箱過程是由box指令來實現的,上一段代碼所對應的IL 代碼如下所示:

在這段IL代碼中,除了有box指令外,我們還看到了一個unbox指令,正如其字面意思所提示的一樣,該指令就是完成拆箱操作的IL指令。
拆箱過程也可以具體分為3個步驟:
(1)檢查實例:首先檢查要進行拆箱操作的引用類型變量是否為null,如果為null則拋出空指針異常,如果不為null則繼續減產變量是否合拆箱后的類型是同一類型,若不是則會拋出InvalidCastExce異常
(2)地址返回:返回已裝箱變量的實際數據部分地址
(3)數據復制: 將托管堆中的實際數據復制到棧中

?

總結:對于拆箱與裝箱的理解之所以是如此重要,主要是因為裝箱和拆箱操作對性能有很大的影響。 如果程序代碼中存在過多的裝箱和拆箱操作,由于兩個過程
都需要進行數據復制,該操作會消耗大量額外運行時間;并且裝箱和拆箱必然會產生多余的對象,這進一步加重了GC的負擔,導致程序的性能降低。此外,還會引起一些隱藏的bug。

所以我們在寫代碼時,應盡量避免裝箱拆箱操作,最好使用泛型來編程。當然泛型的好處不止于此,泛型還可以增加程序的可讀性,使程序更容易被復用等等,至于泛型以后再做詳細介紹.

?

更多內容請參考:http://www.cnblogs.com/ludbul/p/4466522.html 《C#中如何正確的操作字符串?》

?


3,常量與變量
這里主要講一下靜態常量const和動態常量readonly

?

1)const修飾的常量在聲明的時候必須初始化;readonly修飾的常量則可以延遲到構造函數初始化
2)const修飾的常量在編譯期間就被解析,即常量值被替換成初始化的值;readonly修飾的常量則延遲到運行的時候
此外const常量既可以聲明在類中也可以在函數體內,但是static readonly常量只能聲明在類中。

?

1 using System; 2 class P 3 { 4 static readonly int A=B*10; 5 static readonly int B=10; 6 public static void Main(string[] args) 7 { 8 Console.WriteLine("A is {0},B is {1} ",A,B); 9 } 10 }

?

Result:A is 0, B is 10;

?

1 using System; 2 class P 3 { 4 const int A=B*10; 5 const int B=10; 6 public static void Main(string[] args) 7 { 8 Console.WriteLine("A is {0},B is {1} ",A,B); 9 } 10 }

?

Result:A is 100, B is 10;


解析:
那么為什么是這樣的呢?其實在上面說了,const是靜態常量,所以在編譯的時候就將A與B的值確定下來了(即B變量時10,而A=B*10=10*10=100),那么Main函數中的輸出當然是A is 100,B is 10啦。而static readonly則是動態常量,變量的值在編譯期間不予以解析,所以開始都是默認值,像A與B都是int類型,故都是0。而在程序執行到A=B*10;所以A=0*10=0,程序接著執行到B=10這句時候,才會真正的B的初值10賦給B。

?


4,運算符重載
運算符重載只能用于類或結構中,通過類或結構中聲明一個名為operator x的方法,即可完成一個運算符的重載。

?

先來看幾行簡單的代碼:

1 static void Main(string[] args) 2 { 3 int x = 5; 4 int y = 6; 5 int sum = x + y; 6 Console.WriteLine(sum); 7 Console.ReadLine(); 8 }

?

?

一個int sum=x+y; 加法運算。

?

稍微封裝一下:

1 static void Main(string[] args) 2 { 3 int x = 5; 4 int y = 6; 5 int sum = Add(x, y); 6 Console.WriteLine(sum); 7 } 8 9 static int Add(int x, int y) 10 { 11 return x + y; 12 }

?

?

如果現在有一個類,需要得知兩個類某個屬性的和,我們可能會這樣:

?

1 public class Person 2 { 3 public string Name { get; set; } 4 public int Age { get; set; } 5 public Person(string name, int age) 6 { 7 this.Name = name; 8 this.Age = age; 9 } 10 } 11 12 class Program 13 { 14 static void Main(string[] args) 15 { 16 Person p1 = new Person("aehyok", 25); 17 Person p2 = new Person("Leo", 24); 18 int sum = Add(p1.Age, p2.Age); 19 Console.WriteLine(sum); 20 } 21 22 static int Add(int x, int y) 23 { 24 return x + y; 25 } 26 }

?

?

我們再來改動一下:

1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Person p1 = new Person("aehyok", 25); 6 Person p2 = new Person("Leo", 24); 7 int sum = p1 + p2; 8 Console.WriteLine(sum); 9 } 10 } 11 12 public class Person 13 { 14 public string Name { get; set; } 15 public int Age { get; set; } 16 public Person(string name, int age) 17 { 18 this.Name = name; 19 this.Age = age; 20 } 21 22 public static int operator +(Person p1,Person p2) 23 { 24 return p1.Age+p2.Age; 25 } 26 }

?

?

5。static字段和static構造函數
主要來說明執行的順序:
  1、編譯器在編譯的時候,先分析所需要的靜態字段,如果這些靜態字段所在的類有靜態的構造函數,那么就會忽略字段的初始化;如果沒有靜態的構造函數,那么就會對靜態字段進行初始化。
  2、如果存在多個靜態類,那么初始化的靜態成員的順序會根據引用的順序,先引用到的先進行初始化,但如果類的靜態成員的初始化依賴于其他類的靜態成員,則會先初始化被依賴的靜態成員。
  3、而帶有靜態構造函數的類的靜態字段,只有在引用到的時候才進行初始化。

?

1 public class A 2 { 3 public static int X = B.Y+1; 4 static A() { } 5 } 6 7 public class B 8 { 9 public static int Y=A.X+1; 10 } 11 12 class Program 13 { 14 static void Main(string[] args) 15 { 16 Console.WriteLine("A.X={0},B.Y={1}",A.X,B.Y); 17 Console.ReadLine(); 18 } 19 }

?

?

執行結果是:A.X = 1, B.Y = 2;

?

結果如何呢?再來看第二個小例子:

1 public class A 2 { 3 public static int X = B.Y+1; 4 } 5 6 public class B 7 { 8 public static int Y=A.X+1; 9 static B() { } 10 } 11 12 class Program 13 { 14 static void Main(string[] args) 15 { 16 Console.WriteLine("A.X={0},B.Y={1}",A.X,B.Y); 17 Console.ReadLine(); 18 } 19 }

?

?

執行結果是:A.X = 2, B.Y = 1;
是否和你想的結果一致呢?其實分析這兩種情況 只要記住第一條概念就好:如果這些靜態字段所在的類有靜態的構造函數,那么就會忽略字段的初始化;如果沒有靜態的構造函數,那么就會對靜態字段進行初始化。

?

看第一段代碼片斷:
A:
public static int X = B.Y+1;
static A() { }

?

B:
public static int Y=A.X+1;

?

A.X B.X ==> 先調用A.X, A中int X = B.Y + 1; 所以會接著調用B.Y, 因為B中無靜態的構造函數,所以就會對靜態字段進行初始化。 int Y = 0; 故 X = 1; B.Y = 2;

?

看第二段代碼片斷:
A:
public static int X = B.Y+1;

?

B:
public static int Y=A.X+1;
static B() { }

?

A.X B.X ==> 先調用A.X, A中int X = B.Y + 1; 所以會接著調用B.Y, 因為B中有靜態的構造函數,所以就會忽略字段的初始化。 int Y = 1; 故 X = 2; B.Y = 2;
大家如果有興趣的話也可以設置斷點查看下代碼是如何運行的。

?

轉載于:https://www.cnblogs.com/wang-meng/p/5143732.html

總結

以上是生活随笔為你收集整理的[读书笔记]C#学习笔记三: C#类型详解..的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。