C#類型本質-進階編程篇(一)
這是我的第一篇講解編程技巧的教程文章,不管是措辭還是排版,肯定有諸多不完善的地方,還請各位多多擔待。對於專業知識來說,因為技術是會存在過時的,所以我也沒有辦法保證所講解的內容完全是正確的,而且我覺得我理解的還不夠深,將來的某一天可能也會回頭修正現在的表述,說明,也就是傳說中的打臉,但是這件事情總是要有個開端的,既然想到這個問題,就何不從現在開始去做呢。
這個系列的文章在專業性方面,我會儘可能的講解我清楚的,如果我自己也不清楚,我會參考一些書籍,如果還沒有書籍參考,那我就通過自己的經驗來做了,大體上我會為我的教程負責的,希望可以幫到目前正在編程或將要編程的你們,你們是最可愛的人。
下面進入正題,也就是進階編程篇的第一篇,C#類型的本質,本來本章也想了另一個名稱:你真的了解類型嗎?後來覺得太裝B了,所以仍然使用了C#本質的標題。要進入本片學習,我需要假定您已經看過基礎的C#編程書了,至少會簡單的拖控制項開發了,可以顯示顯示的數據,也會自定義控制項的開發。正式進入提高階段,如果您沒有基礎的知識,推薦您去學習《C#從人們到精通》這本書了,OK,馬上進入正題。
我們都知道日常使用的類型,比如byte,int,long,等,除了這些值類型之外,還有各種庫提供的類和我們自己開發的類。比如
Timer time1=new Timer();
我們非常習慣使用這些類型來創建示例,並且使用的很自然,比如
int i = 0;
OK,那我們來講解第一個問題,不知道你有沒有發現,在visual studio開發程序的時候,對於我們熟悉的int類型,IDE提供了兩套類型模式,比如int和Int32,根據查看MSDN的說法,兩者是一致的,如果真的是一致的,又為什麼提供兩種命名方式呢,這個問題的解釋有點複雜,關係到程序運行的機制,而C#只是一種語言,一種編寫CLR程序的語言,VS將我們用C#編寫的程序翻譯成了IL中間語言的程序,因為CLR和IL語言是無縫集成的,這樣我們的程序才可以在CLR上運行,可能有些人會疑問,我們寫的程序不應該是windows來運行的嗎,本質上確實是如此,由windows來運行CLR,CLR來運行託管程序。那麼問題來了,IL中間語言本身就直接支持了一套類型,比如Int32,Int16,Int64,等等,這套類型叫做基元類型,在微軟開發C#編譯器的時候,腦子一熱,覺得原來的名稱不太符合C語言的習慣,所以就額外提供了一套新的類型命名,int,short,long等,在編譯的時候直接會編譯成CLR支持的類型,至於在使用習慣上,根據自己的習慣選擇,有些書會比較推薦CLR的標準。
第二個有意思的問題是如果我寫了個靜態的int變數,就可以在程序的任何地方(可以在不同的線程)進行引用,獲取,設置值而不用擔心其他問題,比如競爭問題。不要思考也還好,一旦要去考慮這個問題的答案,背後就隱藏了一個極大的秘密,對象數據的本質。我們在實例化一個對象後,如下
int i=0;
這行代碼不僅僅只是生成了一個4個位元組的變數數據,準確的說,對象i的數據部分確實是4個位元組而已,但是對象本身絕對不是4個位元組的問題,它還有另外兩個非常重要的數據對象,叫同步索引塊和類型對象塊,而其中的同步索引塊就控制了類型在同一瞬間只能進行一次設置,我們知道數據都是01組成,我們在執行i=0xffffff時,在另一個地方剛好獲取i的值,這樣就避免了萬一設置到一半(i=0xff0000),我們就獲取到了錯誤的值的可能性。
第三個有意思的問題是對象其實知道它自己的類型,還是拿上述的
int i=0;
作為說明對象,我們可以調用i.GetType()來獲取i本身的類型,你可能會覺得這玩意到底有什麼用,我自己定義的i我還不知道他是什麼類型嗎?
事實上用處大了,我先說明有什麼用處,在說明原因。正是因為對象自己知道自己的類型,才能執行一些類型的轉換,強制轉換也好,隱式轉換也罷,C#所有的轉換建立在這個基礎之上的,再看下面的代碼:
int i=0;
object obj=(object)i;
string m=(string)obj;
在第二行代碼中,因為編譯器知道object是所有類的基類,所以可以轉化,但是obj對象的類型真的是object嗎?答案是不一定的,因為object是所有類的基類,所以obj理論上來說可以是任何類型,此處你可以獲取類型來確認,obj其實是int類型。正是因為int類型和string類型不存在繼承關係,所以第三行代碼報錯。
第四個有意思的問題是,我們都知道int使用了四個位元組,byte使用了一個位元組,但是如果我問bool使用了幾個位元組?還是一個位而已?這個問題就需要實踐出真知了,我們就寫代碼來判斷吧,聲明一個類
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Explicit)]
public class SomeValType
{
[System.Runtime.InteropServices.FieldOffset(0)]
public byte ValueByte = 0;
[System.Runtime.InteropServices.FieldOffset(0)]
public int ValueInt = 0;
[System.Runtime.InteropServices.FieldOffset(0)]
public bool ValueBool = false;
}
然後在其他地方使用斷點查看,代碼如下:
SomeValType value = new SomeValType();
value.ValueInt = -1;
value.ValueBool = false;
這時候監視查看到bool對象隻影響了一個位元組。這種技術允許數據區域重疊,你改變一個變數的時候也會影響其他變數,有些情況會比較適合,比如是一個IP類型,由四個位元組組成,一個int就能表示,但也允許設置單個的網段。
第五個有意思的類型就是string了,看似平凡的字元串,背後的東西多的不得了,字元串類型有個非常大的特性,它是不易變的。如果你定義了一個字元串string m="123456";,它就傻傻的呆在一個內存塊中,不會變化,直到被清除為止,所以針對如下代碼怎麼去理解:
string s = "";
for (int i = 0; i < 10000; i++)
{
s += "1";
}
s的最終結果是10000個1組成的字元串,但是中間有一萬次的重新分配和刪除,實際的性能非常差,應該避免這種情況。
關於string類型最難的就是本地化了,雖然大多數的程序員都不太關心這個問題,因為大多數的程序都只是給一個特定語言使用的,比如說中文,比如說英文,所以此處就簡單的提個例子,即時兩個看著不同的string,因為語言文化不一致,在比較相同的時候也是可能相同的。
第一篇文章就大致寫到這裡,以後有機會再補充。


※XSD 雜項 數據類型
※自製Monkey編程語言編譯器:增加數組操作API和Mapsh數據類型
※RADIX DLT——一種全新類型的加密貨幣
※類型論:一類新的數學
※R語言:數據類型(向量、數組、矩陣、 列表和數據框)
※一文詳解 React 組件類型
※GTA5MOD安裝視頻教程 全部類型安裝教程
※XSD 字元串 數據類型
※Mariadb學習總結(三):數據類型
※影評:類型文學的文學化——評《齊瑪的作品》
※MATLAB入門篇 數值類型
※典型案例3/10 (類型:體育類,學校:丹佛大學)
※編程教程:淺談C#的隱式顯式類型轉換
※《動物世界》:作者、類型與改編
※XML DOM 節點類型
※NBA球衣等級類型詳解
※窗帘軌道的基本類型
※XSD 數值數據類型
※opencv Mat類型和BYTE*指針類型互轉
※Kotlin 基本數據類型