在C#中,靜态和非靜态的特征對于我們來說是再熟悉不過了,但是很少看到有一篇文章去好好地總結靜态和非靜态它們之間的不同,為了幫助大家更好地去了解靜态和非靜态特征, 是以将在這篇文章中幫大家全面總結下它們之間的不同,包括靜态類,靜态成員和靜态構造函數。希望在大家鞏固基礎的時候可以拿出來好好複習下的。下面廢話不多了,直接進入我們今天的主題。
在自定義類或看.NET Framework類庫中都可以發現,類中大部分都是具體執行個體特征(也就是沒有static辨別的),同時我們也能看到一些具有靜态特征的類或成員,例如我們經常使用的Console類以及WriteLine方法就是靜态的。然而有些朋友會疑惑,為什麼還要有靜态特征的呢?幹脆都定義為執行個體的好了? 然後靜态特征的存在肯定有它存在的原因的,并不是我們就是要這麼定義的,其實我一直認為不管是什麼都是源于生活的, 技術的實作也是一樣,比如我們開發程式,需要掌握技術外,其實更重要的是業務邏輯這塊的,如果你都不知道你開發的東西是怎樣的一個流程,即使你技術再牛做出來的東西都是反人類的東西(也就是指不符合使用者的使用者習慣和之前的一個業務需求),其實靜态特征的存在也是源于生活的,對于類好比就是我們現實生活中的人或事物,靜态特征和非靜态特征就好比生活中人或事物具有的特征, 我們詢問人的時候或者電視劇警察查案件的時候,都會聽到這樣一句話 "那個人有什麼特征?"或 “嫌疑犯有什麼特征?多高,年齡等” 其實高度、年齡、性别都是一個人的特征,是以這些在語言範疇就需要為其進行定義了,也就是我們定義的執行個體成員了,然而有些特征需要被所有對象執行個體所共有的,這些特征在語言範疇就定義為靜态特征,具體哪些特征可以定義為靜态特征呢? 其實這點一樣是源于生活的,是以我們在開發軟體的過程中,必不可少的一個流程就是需求分析了,隻有在了解客戶需求的條件下才能進行之後的所有流程的, 例如一個班級有很多學生,每個學生是一個實體,在語言範疇就可以定義一個類,當我們需要一個學生的時候就可以通過new 關鍵字建立一個出來(說到這裡又讓我想到了惡搞泰囧的圖檔——你有對象嗎?沒對象,你們程式員可以自己new一個啊?),然而我們建立出來的學生他們都有一些共有的特征,如同一個班級,學校等, 如果我們把班級、學校這樣的特征也定義為執行個體的話,那麼我們不是每次建立對象執行個體的時候都為這些共有的特征配置設定一次記憶體的,這樣不僅對記憶體空間的浪費也是不滿足生活常識的,此時我們就可以把班級、學校這樣的特征定義為靜态特征,這樣所有執行個體都可以共享這兩個特征,并且不需要為每個對象執行個體配置設定記憶體。
靜态類和非靜态類在C#中定義基本是一樣的,隻是靜态類定義需要加上static修飾符而已。下面就直接總結下它們之間的差別:
靜态類隻能包含靜态成員,否則會抛出編譯錯誤;然而非靜态類既可以包含非靜态成員也可以包含靜态成員
靜态類是不能執行個體化,之是以不能執行個體化,是因為靜态類會導緻C#編譯器将該類同時标記為abstract和sealed,并且編譯器不會在類型中生成一個執行個體的構造函數,進而導緻靜态類不能執行個體化,具體原因可以見下圖;非靜态類可以,并且靜态成員的通路隻能通過類來進行通路,因為靜态成員是屬于類的。
上面代碼用IL反彙程式設計式得到的IL代碼結構為:

靜态構造函數用來初始化類中的靜态成員的,包括靜态字段和靜态屬性,并且靜态構造函數是不能帶有參數、不能有通路修飾符,靜态構造函數的調用是由CLR第一次調用類成員之前執行的。
下面還是直接總結下靜态構造函數與執行個體構造函數之間的差別:
靜态構造函數可以與無參的執行個體構造函數同時存在
靜态構造函數在CLR加載類時執行,然而執行個體構造函數在每次執行個體建立時都會執行
靜态構造函數隻能對靜态成員初始化,不能對非靜态成員進行初始化操作,然而執行個體構造函數,既可以初始化執行個體成員也可以初始化靜态成員,但靜态隻讀字段除外
靜态構造函數隻被執行一次,但是CLR也不能确定它什麼時候被執行,它的執行方式有兩種,precise和before-field-init,這個會在下一篇文章中詳細給大家介紹,這裡先提出給大家一個思考的空間。而執行個體構造函數在每次建立對象執行個體時都會被執行,建立幾個就會執行幾次
一個類隻能有一個靜态構造函數,卻可以有多個執行個體構造函數
靜态字段的初始值在靜态構造函數調用之前被指定,構造函數的執行順序大緻如下圖所示:
下面就直接總結下它們之間的差別:
靜态成員包括靜态字段和靜态字段,靜态字段一般實作為private,靜态屬性一般實作為public,進而來展現類的封裝性
靜态成員和類相關聯,不依賴于對象而存在,隻能由類來通路;執行個體成員與具體類相關聯,隻能由對象執行個體通路
靜态成員不管建立多少執行個體對象,都在記憶體中隻有一份,執行個體成員每建立一個執行個體對象,都會在記憶體中配置設定一塊記憶體區域。
類似于靜态字段和屬性,靜态方法共享代碼段,同樣以static關鍵字來辨別靜态方法,對于他們之間的差別總結為:
靜态方法隻能通路靜态成員和方法,但是可以間接通過建立執行個體對象來通路執行個體字段、屬性和方法;執行個體方法既可以通路執行個體成員也可以通路靜态成員
靜态方法由類方法‘執行個體方法由對象通路
靜态方法不能引用this關鍵字,而執行個體方法可以
靜态方法不能被辨別為virtual、abstract或override,靜态方法可以被派生通路,但是不能被派生類重寫
Main方法為靜态的,是以Main方法不能直接通路類中的執行個體字段、屬性和方法,否則編譯器會報錯
靜态方法一般用于作為通用的工具類來實作
在性能上,靜态方法和執行個體方法的差别不大。因為,它們都是在JIT加載類的時候配置設定記憶體的,不同的是靜态方法是以類為引用,而執行個體方法是以對象為引用,建立執行個體時,不會再為靜态方法配置設定記憶體,所有執行個體對象共用一個類的方法代碼,是以,靜态方法和執行個體方法的調用,差別僅在于靜态方法可以直接調用,而執行個體方法需要目前對象指針指向該方法,在性能上差不并不大。
到這裡,本文章的内容就介紹完了,通過對靜态特征和非靜态特征的由來來揭開一些都是源于生活的觀點,然後再詳細分析了靜态特征與非靜态特征在C#語言中的差別,希望這些總結可以幫助大家在複習基礎知識的時候可以有用。同時也是自己的一個複習筆記的。
本文轉自LearningHard 51CTO部落格,原文連結:http://blog.51cto.com/learninghard/1224026,如需轉載請自行聯系原作者