本節內容:
1.什么是類型(Type)
2.類型在C#語言中的作用
3.C#語言的類型系統
4.變量、對象與內存
1.什么是類型(type)
類型又名數據類型(Date Type),是數據在內存中存儲時的“型號”,小內存容納大數據會丟失精確度、發生錯誤大內存存納小尺寸數據會導致浪費,編程語言的數據類型與數據的數據類型不完全相同
1.2.強類型語言(保證了數據的完整性)(數據受到數據類型的約束嚴格)與弱類型語言(靈活性與危險性並存)(保證數據的正確性)的比較
*C語言中這里的if里面得的值不為0其他都為真,即使不是bool類型也可通過編譯,故為弱類型;
*相對的C#中if條件里面必須為bool類型的值,否則編譯不過去。
C語言舉例-if條件;JavaScript示例:動態類型(極弱類型語言);C#語言對弱類型?/動態類型的模擬
2.類型在C#語言中的作用
2.1.一個C#類型中所包含的信息有:
①存儲此類型變量所需的內存空間大小;
②此類型值可表示的最大、最小值范圍;
③此類型所包含的成員(如方法、屬性、事件等);
④程序運行的時候,此類型的變量在分配內存的什么位置;
Stack簡介:比較小,比較快,用於方法(函數)調用,(裝多了會爆);
第一種情況為:算法沒寫好,函數調用過多,棧就爆了;
第二種情況:程序有錯誤,不小心在棧上分配了太多的內存,棧就爆了;
稱為:Stack overflow。
Heap簡介:即堆,比較大,用於存儲對象(實例就放在堆里面),如果忘記回收對象會造成內存浪費,這種浪費學名叫做:內存泄漏。
使用Performance Monitor查看進程的堆(Heap)內存使用量;
關於內存泄漏:C#中存在垃圾回收機制,對象沒人用了,有垃圾回收機制自動回收對象的內存。不用像java一樣需要手動釋放內存,換句話說C#較安全比C++安全得多。
⑤此類型所允許的操作符(運算)如: double result = 3 / 4;結果為0
而double result = 3.0 / 4.0;結果為0.75;
⑥靜態的程序指的是沒有在運行的程序。
程序的靜態期(編輯器和編譯器),動態期(運行期):靜態的分析代碼,動態的調試程序 。
可以這么說靜態的程序是裝在硬盤里的,動態的程序是裝在內存里的。
雙擊一個應用程序(exe)就是一個把存儲在硬盤里的靜態程序動態加載到內存的過程。
Process(進程)概念介紹:一個程序從硬盤加載到內存開始執行之后,就形成一個進程,換句話說就是這個程序正在運行的實例。進程名與(exe)程序文件名字相同,且每個進程都有一個pid(Process ID)
Windows+R鍵可以打開運行窗口,輸入perfmon(Performance Monitor)進入性能監視器,打開性能監視器窗口,該窗口默認監視總的進程,可以點“x”在“+”號中添加具體的進程,監視某一程序的內存占比變化。
3.C#語言的類型系統
①C#五大數據類型
類(Class):如Windows、Form、Console等
結構體(Structures):如Int32、Int64、Single、Double
如:在int中f12查看定義:public struct Int32 : IComparable
枚舉(Enumerations):如HorizontalAlignment、Visbility
例如:public enum FormWindowState
{
//默認大小的窗口。
Normal = 0,
//最小化的窗口。
Minimized = 1,
//最大化的窗口。
Maximized = 2,
}
接口(Interfaces)
委托(Delegates)
②C#類型的派生譜系:
Object:引用類型(Reference Type)和值類型(Value Type)
引用類型:類、接口、委托
值類型:結構體、枚舉
最右邊的分類上下來看可以分為三組:第一組(與引用類型相關):object、string(這兩個關鍵詞基類(class)為本身)與class 、interface、delegate(這三個關鍵字不是具體的數據類型f12找不到定義的,而是運用這三個關鍵詞去創建他們的數據類型);
第二組(與值類型相關):橫線以上:對應的是真正數據類型;橫線以下與第一組的橫線以下的三個一樣,都是去定義各自的類型,沒有基類型。
第三組:true和false為bool類型的返回值;void表示不需要返回值,null表示一個引用變量里面的值是空的不屬於任何數據類型;var與dynamic是用來聲明變量的(以后會用到)。
(藍色的字體表示為現成的數據類型,而且十分常用,甚至被歸為關鍵字了;其次這些數據類型都是基本數據類型(即別的類型都是拿這些數據類型去構成的))
4.變量、對象與內存
*值類型與引用類型的內存存儲方式是不同的,不分清將會造成很大錯誤!
①什么是變量
表面上來看(從C#代碼的上下文行文來看),變量的用途是存儲數據。
實際上,變量表示了存儲位置(例如int x=100;x只是一個標簽指向一個地址,數據100就存儲在這個地址里面),並且每一個變量都有一個存儲類型,以決定什么類型的值能夠存入變量。*即變量名稱表示(對應着)變量的值在內存中的存儲位置
*認識一個事物是由淺入深,由表及里,由現象到本質的過程。
變量一共有7種:
靜態變量(static),實例變量(也叫非靜態變量)(成員變量、字段),數組元素、值參數、引用參數、輸出形參、局部變量。
狹義的變量指的是局部變量,因為其它種類的變量都有自己的約定名稱。
簡單地講,局部變量就是方法體(函數體)里面聲明的變量。
變量的聲明:
有效的修飾符組合(可無) 類型 變量名 初始化器(可無)如:int x;
*總結:變量=以變量名所對應的內存地址為起點,以其數據類型所要求的存儲空間為長度的一塊內存區域
②值類型的變量:
以byte/sbyte/short/ushort為例:
值類型沒有實例,所謂“實例”與變量合二為一(因為是結構體struct)。
值類型變量的存儲如:byte b;b=100;這兩條語句作用為:首先確定byte數據類型需要的存儲空間大小,然后去棧內存中找可以使用的(空的)內存地址,找到內存地址后,就把變量名與該內存地址對應起來,再把100轉化為2進制數據寫進該地址內存中。
③引用類型的變量與實例:
引用類型變量與實例的關系:引用類型變量里存儲的數據是對象(實例)的內存地址解釋如下:存儲引用類型變量與實例時如:
*創建Student類的引用類型變量:Student stu;創建后便去棧內存(因為其為局部變量故由棧分配內存)中尋找空閑的內存並分得4個字節的內存空間(凡是引用類型固定分配4字節)。
*然后創建Student類的實例:new Student();這個時候要去找空閑的堆內存,找 到之后開始分配內存,這時需要計算需要多大的內存(類中的各種數據占內存的總 和),分完之后還要在該內存塊內部進行分組:這塊內存是存儲哪種數據類型的, 那塊屬於哪種數據類型...。
*最后將其賦值給引用類型變量(把氣球給孩子):stu = new Student();此時在內 存中把創建類的實例時分配到的內存地址(內存地址是一個編號不是二進制數)轉化為二進制數,並將該二進制數存儲到創建Student類引用類型變量stu時分配到的4個字節的內存空間里面(按高低原則)。完成引用類型變量的存儲。
這樣就可以解釋兩個不同的引用類型變量來引用同一個實例的情況(兩個小朋友分別用自己的兩根繩子牽着同一個氣球),如下:
{
Student stu1;
Student stu2;
stu1=new Student();
Stu2=stu1;
}
④變量的默認值(局部變量(main函數外如類里面的變量)一旦創建后在內存塊中全部刷為0);注意本地變量不能沒有初始值(main函數里的變量),例如:
所謂的本地變量不能沒有初始值,指的是不能沒有初始值就被其他方法調用,如main函數中的如下語句:
Int x;
Console.WriteLind(x);
由於本地變量x沒有初始值就被Console類的WriteLine方法調用了故會報錯。
⑤常量(值不可改變的變量)
⑦局部變量是在Stack(棧)上分配內存的(其他一般在堆中分配)
⑥裝箱與拆箱(Boxing&Unboxing)
補充說明:object是類可以派生出:數值類型和引用類型,但本身是類而類屬於引用類型,所以object屬於引用類型:內存空間存儲被引用對象的地址。其定義為:
public class Object
{
public Object();
}
可見object一般當做引用類型使用。
*裝箱:當引用變量引用棧上的數據的時候就要進行裝箱,先把被引用的數據(下面指int x)拷貝一份,封裝成一個實例,放在堆上的空閑內存中。
例如 {
int x=100;//注意局部變量是在棧上分配內存的
object obj;
obj=x;
}
為了使“ obj=x;”成立:obj為引用型變量分配到的4個字節內存存的是地址,如果直接把數值型變量x內存里存的數據復制給obj的內存空間,則obj會把內存空間里的數據當做是地址,然后去引用該地址的數據,顯然錯誤。
正確做法為:先在棧上開辟兩塊內存空間給int x和obj(局部變量)再去堆中創建int 類型x的內存空間並把內容復制到里面去,然后把該段內存首地址轉化為2進制數在復制給在棧上obj的內存,這個過程使“obj=x;語句合法的內存操作”叫做裝箱。
*即把棧上的值類型的數據封裝成一個object類型的實例放在堆上叫做裝箱
*拆箱:例如 {
int x=100;
object obj;
obj=x;
Int y=[int]obj;
}
為了使“ Int y=[int]obj;(在堆內把obj的數據取出來)”成立:先在棧內存中找到空閑的內存開辟一個4字節(因為y為int型)的存儲空間存放數值類型變量y,然后將某塊堆內存中存放的obj對象數據的按int32格式把數據(轉換)復制到y變量的棧內存空間中。
*即把堆中obj實例里面的值按要求拆成目標數據類型存儲到棧上y(目標數據類型)的內存空間中。
*裝箱裝地址,拆箱按要求拆出數據(如:int y=obj;則obj要按int32的格式拆出數據)
*裝箱和拆箱會損失程序的性能,慎用。