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

## 3.2 靜態構造函數與實例構造函數
* 靜態構造函數用來初始化類中的靜態成員的,包括靜態字段和靜態屬性,并且靜態構造函數是不能帶有參數、不能有訪問修飾符,靜態構造函數的調用是由CLR第一次調用類成員之前執行的。
* 下面還是直接總結下靜態構造函數與實例構造函數之間的區別:
* 靜態構造函數可以與無參的實例構造函數同時存在
* 靜態構造函數在CLR加載類時執行,然而實例構造函數在每次實例創建時都會執行
* 靜態構造函數只能對靜態成員初始化,不能對非靜態成員進行初始化操作,然而實例構造函數,既可以初始化實例成員也可以初始化靜態成員,但靜態只讀字段除外
* 靜態構造函數只被執行一次,但是CLR也不能確定它什么時候被執行,它的執行方式有兩種,precise和before-field-init,這個會在下一篇文章中詳細給大家介紹,這里先提出給大家一個思考的空間。而實例構造函數在每次創建對象實例時都會被執行,創建幾個就會執行幾次
* 一個類只能有一個靜態構造函數,卻可以有多個實例構造函數
靜態字段的初始值在靜態構造函數調用之前被指定,構造函數的執行順序大致如下圖所示:

## 3.3 靜態字段、屬性和實例字段、屬性
下面就直接總結下它們之間的區別:
* 靜態成員包括靜態屬性和靜態字段,靜態字段一般實現為private,靜態屬性一般實現為public,從而來體現類的封裝性
* 靜態成員和類相關聯,不依賴于對象而存在,只能由類來訪問;實例成員與具體類相關聯,只能由對象實例訪問
* 靜態成員不管創建多少實例對象,都在內存中只有一份,實例成員每創建一個實例對象,都會在內存中分配一塊內存區域。
## 3.4 靜態方法與實例方法
類似于靜態字段和屬性,靜態方法共享代碼段,同樣以static關鍵字來標識靜態方法,對于他們之間的區別總結為:
* 靜態方法只能訪問靜態成員和方法,但是可以間接通過創建實例對象來訪問實例字段、屬性和方法;實例方法既可以訪問實例成員也可以訪問靜態成員
* 靜態方法由類方法‘實例方法由對象訪問
* 靜態方法不能引用this關鍵字,而實例方法可以
* 靜態方法不能被標識為virtual、abstract或override,靜態方法可以被派生訪問,但是不能被派生類重寫
* Main方法為靜態的,所以Main方法不能直接訪問類中的實例字段、屬性和方法,否則編譯器會報錯
* 靜態方法一般用于作為通用的工具類來實現
* 在性能上,靜態方法和實例方法的差別不大。因為,它們都是在JIT加載類的時候分配內存的,不同的是靜態方法是以類為引用,而實例方法是以對象為引用,創建實例時,不會再為靜態方法分配內存,所有實例對象共用一個類的方法代碼,所以,靜態方法和實例方法的調用,區別僅在于靜態方法可以直接調用,而實例方法需要當前對象指針指向該方法,在性能上差不并不大。
## 四、小結
到這里,本文章的內容就介紹完了,通過對靜態特征和非靜態特征的由來來揭開一些都是源于生活的觀點,然后再詳細分析了靜態特征與非靜態特征在C#語言中的區別,希望這些總結可以幫助大家在復習基礎知識的時候可以有用。同時也是自己的一個復習筆記的。
- C# 基礎知識系列
- C# 基礎知識系列 專題一:深入解析委托——C#中為什么要引入委托
- C# 基礎知識系列 專題二:委托的本質論
- C# 基礎知識系列 專題三:如何用委托包裝多個方法——委托鏈
- C# 基礎知識系列 專題四:事件揭秘
- C# 基礎知識系列 專題五:當點擊按鈕時觸發Click事件背后發生的事情
- C# 基礎知識系列 專題六:泛型基礎篇——為什么引入泛型
- C# 基礎知識系列 專題七: 泛型深入理解(一)
- C# 基礎知識系列 專題八: 深入理解泛型(二)
- C# 基礎知識系列 專題九: 深入理解泛型可變性
- C#基礎知識系列 專題十:全面解析可空類型
- C# 基礎知識系列 專題十一:匿名方法解析
- C#基礎知識系列 專題十二:迭代器
- C#基礎知識 專題十三:全面解析對象集合初始化器、匿名類型和隱式類型
- C# 基礎知識系列 專題十四:深入理解Lambda表達式
- C# 基礎知識系列 專題十五:全面解析擴展方法
- C# 基礎知識系列 專題十六:Linq介紹
- C#基礎知識系列 專題十七:深入理解動態類型
- 你必須知道的異步編程 C# 5.0 新特性——Async和Await使異步編程更簡單
- 全面解析C#中參數傳遞
- C#基礎知識系列 全面解析C#中靜態與非靜態
- C# 基礎知識系列 C#中易混淆的知識點
- C#進階系列
- C#進階系列 專題一:深入解析深拷貝和淺拷貝
- C#進階系列 專題二:你知道Dictionary查找速度為什么快嗎?
- C# 開發技巧系列
- C# 開發技巧系列 使用C#操作Word和Excel程序
- C# 開發技巧系列 使用C#操作幻燈片
- C# 開發技巧系列 如何動態設置屏幕分辨率
- C# 開發技巧系列 C#如何實現圖片查看器
- C# 開發技巧 如何防止程序多次運行
- C# 開發技巧 實現屬于自己的截圖工具
- C# 開發技巧 如何使不符合要求的元素等于離它最近的一個元素
- C# 線程處理系列
- C# 線程處理系列 專題一:線程基礎
- C# 線程處理系列 專題二:線程池中的工作者線程
- C# 線程處理系列 專題三:線程池中的I/O線程
- C# 線程處理系列 專題四:線程同步
- C# 線程處理系列 專題五:線程同步——事件構造
- C# 線程處理系列 專題六:線程同步——信號量和互斥體
- C# 多線程處理系列專題七——對多線程的補充
- C#網絡編程系列
- C# 網絡編程系列 專題一:網絡協議簡介
- C# 網絡編程系列 專題二:HTTP協議詳解
- C# 網絡編程系列 專題三:自定義Web服務器
- C# 網絡編程系列 專題四:自定義Web瀏覽器
- C# 網絡編程系列 專題五:TCP編程
- C# 網絡編程系列 專題六:UDP編程
- C# 網絡編程系列 專題七:UDP編程補充——UDP廣播程序的實現
- C# 網絡編程系列 專題八:P2P編程
- C# 網絡編程系列 專題九:實現類似QQ的即時通信程序
- C# 網絡編程系列 專題十:實現簡單的郵件收發器
- C# 網絡編程系列 專題十一:實現一個基于FTP協議的程序——文件上傳下載器
- C# 網絡編程系列 專題十二:實現一個簡單的FTP服務器
- C# 互操作性入門系列
- C# 互操作性入門系列(一):C#中互操作性介紹
- C# 互操作性入門系列(二):使用平臺調用調用Win32 函數
- C# 互操作性入門系列(三):平臺調用中的數據封送處理
- C# 互操作性入門系列(四):在C# 中調用COM組件
- CLR
- 談談: String 和StringBuilder區別和選擇
- 談談:程序集加載和反射
- 利用反射獲得委托和事件以及創建委托實例和添加事件處理程序
- 談談:.Net中的序列化和反序列化
- C#設計模式
- UML類圖符號 各種關系說明以及舉例
- C#設計模式(1)——單例模式
- C#設計模式(2)——簡單工廠模式
- C#設計模式(3)——工廠方法模式
- C#設計模式(4)——抽象工廠模式
- C#設計模式(5)——建造者模式(Builder Pattern)
- C#設計模式(6)——原型模式(Prototype Pattern)
- C#設計模式(7)——適配器模式(Adapter Pattern)
- C#設計模式(8)——橋接模式(Bridge Pattern)
- C#設計模式(9)——裝飾者模式(Decorator Pattern)
- C#設計模式(10)——組合模式(Composite Pattern)
- C#設計模式(11)——外觀模式(Facade Pattern)
- C#設計模式(12)——享元模式(Flyweight Pattern)
- C#設計模式(13)——代理模式(Proxy Pattern)
- C#設計模式(14)——模板方法模式(Template Method)
- C#設計模式(15)——命令模式(Command Pattern)
- C#設計模式(16)——迭代器模式(Iterator Pattern)
- C#設計模式(17)——觀察者模式(Observer Pattern)
- C#設計模式(18)——中介者模式(Mediator Pattern)
- C#設計模式(19)——狀態者模式(State Pattern)
- C#設計模式(20)——策略者模式(Stragety Pattern)
- C#設計模式(21)——責任鏈模式
- C#設計模式(22)——訪問者模式(Vistor Pattern)
- C#設計模式(23)——備忘錄模式(Memento Pattern)
- C#設計模式總結
- WPF快速入門系列
- WPF快速入門系列(1)——WPF布局概覽
- WPF快速入門系列(2)——深入解析依賴屬性
- WPF快速入門系列(3)——深入解析WPF事件機制
- WPF快速入門系列(4)——深入解析WPF綁定
- WPF快速入門系列(5)——深入解析WPF命令
- WPF快速入門系列(6)——WPF資源和樣式
- WPF快速入門系列(7)——深入解析WPF模板
- WPF快速入門系列(8)——MVVM快速入門
- WPF快速入門系列(9)——WPF任務管理工具實現
- ASP.NET 開發
- ASP.NET 開發必備知識點(1):如何讓Asp.net網站運行在自定義的Web服務器上
- ASP.NET 開發必備知識點(2):那些年追過的ASP.NET權限管理
- ASP.NET中實現回調
- 跟我一起學WCF
- 跟我一起學WCF(1)——MSMQ消息隊列
- 跟我一起學WCF(2)——利用.NET Remoting技術開發分布式應用
- 跟我一起學WCF(3)——利用Web Services開發分布式應用
- 跟我一起學WCF(3)——利用Web Services開發分布式應用
- 跟我一起學WCF(4)——第一個WCF程序
- 跟我一起學WCF(5)——深入解析服務契約 上篇
- 跟我一起學WCF(6)——深入解析服務契約 下篇
- 跟我一起學WCF(7)——WCF數據契約與序列化詳解
- 跟我一起學WCF(8)——WCF中Session、實例管理詳解
- 跟我一起學WCF(9)——WCF回調操作的實現
- 跟我一起學WCF(10)——WCF中事務處理
- 跟我一起學WCF(11)——WCF中隊列服務詳解
- 跟我一起學WCF(12)——WCF中Rest服務入門
- 跟我一起學WCF(13)——WCF系列總結
- .NET領域驅動設計實戰系列
- .NET領域驅動設計實戰系列 專題一:前期準備之EF CodeFirst
- .NET領域驅動設計實戰系列 專題二:結合領域驅動設計的面向服務架構來搭建網上書店
- .NET領域驅動設計實戰系列 專題三:前期準備之規約模式(Specification Pattern)
- .NET領域驅動設計實戰系列 專題四:前期準備之工作單元模式(Unit Of Work)
- .NET領域驅動設計實戰系列 專題五:網上書店規約模式、工作單元模式的引入以及購物車的實現
- .NET領域驅動設計實戰系列 專題六:DDD實踐案例:網上書店訂單功能的實現
- .NET領域驅動設計實戰系列 專題七:DDD實踐案例:引入事件驅動與中間件機制來實現后臺管理功能
- .NET領域驅動設計實戰系列 專題八:DDD案例:網上書店分布式消息隊列和分布式緩存的實現
- .NET領域驅動設計實戰系列 專題九:DDD案例:網上書店AOP和站點地圖的實現
- .NET領域驅動設計實戰系列 專題十:DDD擴展內容:全面剖析CQRS模式實現
- .NET領域驅動設計實戰系列 專題十一:.NET 領域驅動設計實戰系列總結