.NET、NET Framewor以及.NET Core的關係(一)

什麼是.NET?什麼是.NET Framework?本文將從上往下,按部就班的介紹一系列相關.NET的概念,先從類型系統開始講起,我將經過跨語言操做這個例子來逐漸引入一系列.NET的相關概念,這主要包括:CLS、CTS(CLI)、FCL、Windows下CLR的相關核心組成、Windows下託管程序運行概念、什麼是.NET Framework,.NET Core,.NET Standard及一些VS編譯器相關雜項和相關閱讀連接。完整的從上讀到下則你能夠理解個大概的.NET體系。web

文章是我一字一字親手碼出來的,天天下班用休息時間寫一點,持續了二十來天。且對於文章上下銜接、概念引入花了不少心思,致力讓不少概念在本文中顯得通俗。但畢竟.NET系統很龐大,本文篇幅有限,因此在部分小節中我會給出延伸閱讀的連接,在文章結尾我給出了一些小的建議,但願能對須要幫助的人帶來幫助,若是想與我交流能夠文章留言或者加.NET技術交流羣:166843154編程

.NET和C#是什麼關係

語言,是人們進行溝通表達的主要方式。編程語言,是人與機器溝通的表達方式。不一樣的編程語言,其側重點不一樣。有的編程語言是爲了科學計算而開發的,因此其語法和功能更偏向於函數式思想。有些則是爲了開發應用程序而創立的,因此其語法和功能更爲均衡全面。c#

微軟公司是全球最大的電腦軟件提供商,爲了佔據開發者市場,進而在2002年推出了Visual Studio(簡稱VS,是微軟提供給開發者的工具集) .NET 1.0版本的開發者平臺。而爲了吸引更多的開發者涌入平臺,微軟還在2002年宣佈推出一個特性強大而且與.NET平臺無縫集成的編程語言,即C# 1.0正式版。
只要是.NET支持的編程語言,開發者就能夠經過.NET平臺提供的工具服務和框架支持便捷的開發應用程序。api

C#就是爲宣傳.NET而創立的,它直接集成於Visual Studio .NET中,VB也在.NET 1.0發佈後對其進行支持, 因此這兩門語言與.NET平臺耦合度很高,而且.NET上的技術大多都是以C#編程語言爲示例,因此常常就.NET和C#混爲一談(實質上它們是相輔相成的兩個概念)。
而做爲一個開發者平臺,它不只僅是包含開發環境、技術框架、社區論壇、服務支持等,它還強調了平臺的跨語言、跨平臺編程的兩個特性。數組

跨語言和跨平臺是什麼

跨語言:即只要是面向.NET平臺的編程語言((C#、Visual Basic、C++/CLI、Eiffel、F#、IronPython、IronRuby、PowerBuilder、Visual COBOL 以及 Windows PowerShell)),用其中一種語言編寫的類型能夠無縫地用在另外一種語言編寫的應用程序中的互操做性。
跨平臺:一次編譯,不須要任何代碼修改,應用程序就能夠運行在任意有.NET框架實現的平臺上,即代碼不依賴於操做系統,也不依賴硬件環境。安全

什麼是跨語言互操做,什麼是CLS

每門語言在最初被設計時都有其在功能和語法上的定位,讓不一樣的人使用擅長的語言去幹合適的事,這在團隊協做時尤其重要。
.NET平臺上的跨語言是經過CLS這個概念來實現的,接下來我就以C#和VB來演示 什麼是.NET中的跨語言互操做性。架構

通俗來講,雖然c#和vb是兩個不一樣的語言,但此處c#寫的類能夠在vb中當作自家寫的類同樣正常使用。mvc

好比我在vb中寫了一個針對String的首字母大寫的擴展方法,將其編譯後的dll引用至C#項目中。
app

 

在C#項目中,能夠像自身代碼同樣正常使用來自vb這個dll的擴展方法。框架

 

如今有那麼多面向對象語言,但不是全部編程語言都能這樣直接互操做使用,而.NET平臺支持的C#和VB之因此能這樣無縫銜接,先讀然後知,後文將會介紹原因。不過雖然.NET平臺提供了這樣一個互操做的特性,但終究語言是不同的,每一個語言有其特點和差別處,在相互操做的時候就會不免遇到一些例外狀況。

好比我在C#中定義了一個基類,類裏面包含一個公開的指針類型的成員,我想在vb中繼承這個類,並訪問這個公開的成員。

 

可是vb語言由於其定位不須要指針,因此並無C#中如int*這樣的指針類型,因此在vb中訪問一個該語言不支持的類型會報錯的,會提示:字段的類型不受支持。

再好比,C#語言中,對類名是區分大小寫的,我在C#中定義了兩個類,一個叫BaseBusiness,另外一個叫baseBusiness。我在vb中去繼承這個BaseBusiness類。

 

如圖,在vb中訪問這個類會報錯的,報:"BaseBusiness"不明確,這是由於在vb中對類名是不區分大小寫的。在vb中,它認爲它同時訪問了兩個如出一轍的類,因此按照vb的規則這是不合理的。那麼爲了在vb調用c#的程序集中避免這些因語言的差別性而致使的錯誤,在編寫c#代碼的時候 就應該提早知道vb中的這些規則,來應付式的開發。 

可是,若是我想不只僅侷限於C#和VB,我還想我編寫的代碼在.Net平臺上通用的話,那麼我還必須得知道.NET平臺支持的每一種語言和我編寫代碼所使用的語言的差別,從而在編寫代碼中避免這些。

這幾年編程語言層出不窮,在未來.NET可能還會支持更多的語言,若是說對一個開發者而言掌握全部語言的差別處這是不現實的,因此.NET專門爲此參考每種語言並找出了語言間的共性,而後定義了一組規則,開發者都遵照這個規則來編碼,那麼代碼就能被任意.NET平臺支持的語言所通用。
而與其說是規則,不如說它是一組語言互操做的標準規範,它就是公共語言規範 - Common Language Specification ,簡稱CLS

 

 CLS從類型、命名、事件、屬性、數組等方面對語言進行了共性的定義及規範。這些東西被提交給歐洲計算機制造聯合會ECMA,稱爲:共同語言基礎設施。

就以類型而言,CLS定義了在C#語言中符合規範的類型和不符合的有:

 

固然,就編碼角度而言,咱們不是必需要看那些詳略的文檔。爲了方便開發者開發,.NET提供了一個特性,名叫:CLSCompliantAttribute,代碼被CLSCompliantAttribute標記後,若是你寫的代碼不符合CLS規範的話,編譯器就會給你一條警告。

 

 

值得一提的是,CLS規則只是面向那些公開可被其它程序集訪問的成員,如public、繼承的protected,對於該程序集的內部成員如Private、internal則不會執行該檢測規則。也就是說,所適應的CLS聽從性規則,僅是那些公開的成員,而非私有實現。

 

那麼有沒有那種特殊狀況,好比我經過反射技術來訪問該程序集中,當前語言並不擁有的類型時會發生什麼狀況呢?

答案是能夠嘗試的,如用vb反射訪問c#中的char*指針類型,即便vb中沒有char*這種等價的指針類型,但mscorlib提供了針對指針類型的 Pointer 包裝類供其訪問,能夠從運行時類攜帶的類型名稱看到其本來的類型名。

 

能夠看到,該類中的元素是不符合CLS規範的。

CLS異常

提到特殊狀況,還要說的一點就是異常處理。.NET框架組成中定義了異常類型系統,在編譯器角度,全部catch捕獲的異常都必須繼承自System.Exception,若是你要調用一個 由不遵循此規範的語言 拋出其它類型的異常對象(C++容許拋出任何類型的異常,如C#調用C++代碼,C++拋出一個string類型的異常),在C#2.0以前Catch(Exception)是捕捉不了的,但以後的版本能夠。
在後續版本中,微軟提供了System.Runtime.CompilerServices.RuntimeWrappedException異常類,將那些不符合CLS的包含Exception的對象封裝起來。而且能夠經過RuntimeCompatibilityAttribute特性來過濾這些異常。
RuntimeWrappedException :https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.compilerservices.runtimewrappedexception?view=netframework-4.7.2

那麼,這個段落總結一下,什麼是CLS呢?

在面向.NET開發中,編寫跨語言組件時所遵循的那些共性,那些規範就叫作 Common Langrage Specification簡稱 CLS,公共語言規範 
官方CLS介紹:https://docs.microsoft.com/zh-cn/dotnet/standard/language-independence-and-language-independent-components

什麼是CTS?

若是理解了什麼是CLS的話,那麼你將很輕鬆理解什麼是CTS。
假設你已經圍繞着封裝 繼承 多態 這3個特性設計出了多款面向對象的語言,你發現你們都是面向對象,都能很好的將現實中的對象模型表達出來。除了語法和功能擅長不一樣,語言的定義和設計結構其實都差很少一回事。

好比,現實中你看到了一輛小汽車,這輛車裏坐着兩我的,那麼如何用這門語言來表達這樣的一個概念和場面?
首先要爲這門語言橫向定義一個「類型」的概念。接下來在程序中就能夠這樣表示:有一個汽車類型,有一我的類型,在一個汽車類型的對象內包含着兩我的類型的對象,由於要表達出這個模型,你又引入了「對象」的概念 。而如今,你又看到,汽車裏面的人作出了開車的這樣一個動做,由此你又引入了「動做指令」這樣一個概念。
接着,你又恍然大悟總結出一個定理,不管是什麼樣的「類型」,都只會存在這樣一個特徵,即活着的 帶生命特徵的(如人) 和 死的 沒有生命特徵的(如汽車) 這二者中的一個。最後,隨着思想模型的成熟,你發現,這個「類型」就至關於一個富有主體特徵的一組指令的集合。
好,而後你開始照葫蘆畫瓢。你參考其它程序語言,你發現你們都是用class來表示類的含義,用struct表示結構的含義,用new來表示 新建一個對象的含義,因而,你對這部分功能的語法也使用class和new關鍵字來表示。而後你又發現,他們還用不少關鍵字來更豐富的表示這些現實模型,好比override、virtual等。因而,在不斷的思想升級和借鑑後,你對這個設計語言過程當中思想的變化仔細分析,對這套語言體系給抽象概括,最終總結出一套體系。

因而你對其它人這樣說,我總結出了一門語言不少必要的東西如兩種主要類別:值類別和引用類別,五個主要類型:類、接口、委託、結構、枚舉,我還規定了,一個類型能夠包含字段、屬性、方法、事件等成員,我還指定了每種類型的可見性規則和類型成員的訪問規則,等等等等,只要按照我這個體系來設計語言,設計出來的語言它可以擁有不少不錯的特性,好比跨語言,跨平臺等,C#和VB.net之因此可以這樣就是由於這兩門語言的設計符合我這個體系。

那麼,什麼是CTS呢?

當你須要設計面向.Net的語言時所須要遵循一個體系(.Net平臺下的語言都支持的一個體系)這個體系就是CTS(Common Type System 公共類型系統),它包括但不限於:

  • 創建用於跨語言執行的框架。

  • 提供面向對象的模型,支持在 .NET 實現上實現各類語言。

  • 定義處理類型時全部語言都必須遵照的一組規則(CLS)。

  • 提供包含應用程序開發中使用的基本基元數據類型(如 Boolean、Byte、Char 等)的庫。

上文的CLS是CTS(Common Type System 公共類型系統)這個體系中的子集。
一個編程語言,若是它可以支持CTS,那麼咱們就稱它爲面向.NET平臺的語言。
官方CTS介紹: https://docs.microsoft.com/zh-cn/dotnet/standard/common-type-system 

微軟已經將CTS和.NET的一些其它組件,提交給ECMA以成爲公開的標準,最後造成的標準稱爲CLI(Common Language Infrastructure)公共語言基礎結構。
因此有的時候你見到的書籍或文章有的只提起CTS,有的只提起CLI,請不要奇怪,你能夠寬泛的把他們理解成一個意思,CLI是微軟將CTS等內容提交給國際組織計算機制造聯合會ECMA的一個工業標準。

什麼是類庫?

在CTS中有一條就是要求基元數據類型的類庫。咱們先搞清什麼是類庫?類庫就是類的邏輯集合,你開發工做中你用過或本身編寫過不少工具類,好比搞Web的常常要用到的 JsonHelper、XmlHelper、HttpHelper等等,這些類一般都會在命名爲Tool、Utility等這樣的項目中。 像這些類的集合咱們能夠在邏輯上稱之爲 "類庫",好比這些Helper咱們統稱爲工具類庫。

什麼是基礎類庫BCL?

當你經過VS建立一個項目後,你這個項目就已經引用好了經過.NET下的語言編寫好的一些類庫。好比控制檯中你直接就能夠用ConSole類來輸出信息,或者using System.IO 便可經過File類對文件進行讀取或寫入操做,這些類都是微軟幫你寫好的,不用你本身去編寫,它幫你編寫了一個面向.NET的開發語言中使用的基本的功能,這部分類,咱們稱之爲BCL(Base Class Library), 基礎類庫,它們大多都包含在System命名空間下。

基礎類庫BCL包含:基本數據類型,文件操做,集合,自定義屬性,格式設置,安全屬性,I/O流,字符串操做,事件日誌等的類型

什麼是框架類庫FCL?

有關BCL的就不在此一一類舉。.NET之大,發展至今,由微軟幫助開發人員編寫的類庫愈來愈多,這讓咱們開發人員開發更加容易。由微軟開發的類庫統稱爲:FCL,Framework Class Library ,.NET框架類庫,我上述所表達的BCL就是FCL中的一個基礎部分,FCL中大部分類都是經過C#來編寫的。

在FCL中,除了最基礎的那部分BCL以外,還包含咱們常見的 如 : 用於網站開發技術的 ASP.NET類庫,該子類包含webform/webpage/mvc,用於桌面開發的 WPF類庫、WinForm類庫,用於通訊交互的WCF、asp.net web api、Web Service類庫等等

什麼是基元類型?

像上文在CTS中提到了 基本基元數據類型,你們知道,每門語言都會定義一些基礎的類型,好比C#經過 int 來定義整型,用 string 來定義 字符串 ,用 object 來定義 根類。當咱們來描述這樣一個類型的對象時能夠有這兩種寫法,如圖:

 

咱們能夠看到,上邊用首字母小寫的藍色體string、object能描述,用首字母大寫的淺藍色String、Object也能描述,這兩種表述方式有何不一樣?

要知道,在vs默認的顏色方案中,藍色體 表明關鍵字,淺藍色體 表明類型。
那麼這樣也就意味着,由微軟提供的FCL類庫裏面 包含了 一些用於描述數據類型的 基礎類型,不管咱們使用的是什麼語言,只要引用了FCL,咱們均可以經過new一個類的方式來表達數據類型。
如圖:

 

用new來建立這些類型的對象,但這樣就太繁瑣,因此C#就用 int關鍵字來表示System.Int32,用 string關鍵字來表示 System.String等,因此咱們才能這樣去寫。

 

像這樣被表述於編譯器直接支持的類型叫作基元類型,它被直接映射於BCL中具體的類。

下面是部分面向.NET的語言的基元類型與對應的BCL的類別圖 :

 

System.Object的意義

提及類型,這裏要說CTS定義的一個很是重要的規則,就是類與類之間只能單繼承,System.Object類是全部類型的根,任何類都是顯式或隱式的繼承於System.Object。

    System.Object定義了類型的最基本的行爲:用於實例比較的Equals系列方法、用於Hash表中Hash碼的GetHashCode、用於Clr運行時獲取的類型信息GetType、用於表示當前對象字符串的ToString、用於執行實例的淺複製MemberwiseClone、用於GC回收前操做的析構方法Finalize 這6類方法。

因此 Object不只是C#語言的類型根、仍是VB等全部面向.NET的語言的類型根,它是整個FCL的類型根。

   固然,CTS定義了單繼承,不少編程語言都知足這個規則,但也有語言是例外,如C++就不作繼承限制,能夠繼承多個,C++/CLI做爲C++在對.NET的CLI實現,若是在非託管編碼中多繼承那也能夠,若是試圖在託管代碼中多繼承,那就會報錯。我前面已經舉過這樣特殊狀況的例子,這也在另外一方面反映出,各語言對CTS的支持並非都如C#那樣全面的,咱們只需明記一點:對於符合CTS的那部分天然就按照CTS定義的規則來。 任何可遵循CTS的類型規範,同時又有.NET運行時的實現的編程語言就能夠成爲.NET中的一員。

計算機是如何運行程序的?

接下來我要說什麼是.NET的跨平臺,並解釋爲何可以跨語言。不過要想知道什麼是跨平臺,首先你得知道一個程序是如何在本機上運行的。

什麼是CPU

CPU,全稱Central Processing Unit,叫作中央處理器,它是一塊超大規模的集成電路,是計算機組成上必不可少的組成硬件,沒了它,計算機就是個殼。
不管你編程水平怎樣,你都應該先知道,CPU是一臺計算機的運算核心和控制核心,CPU從存儲器或高速緩衝存儲器中取出指令,放入指令寄存器,並對指令譯碼,執行指令。
咱們運行一個程序,CPU就會不斷的讀取程序中的指令並執行,直到關閉程序。事實上,從電腦開機開始,CPU就一直在不斷的執行指令直到電腦關機。

什麼是高級編程語言

在計算機角度,每一種CPU類型都有本身能夠識別的一套指令集,計算機無論你這個程序是用什麼語言來編寫的,其最終只認其CPU可以識別的二進制指令集。
在早期計算機剛發展的時代,人們都是直接輸入01010101這樣的沒有語義的二進制指令來讓計算機工做的,可讀性幾乎沒有,沒人願意直接編寫那些沒有可讀性、繁瑣、費時,易出差錯的二進制01代碼,因此後來纔出現了編程語言。

編程語言的誕生,使得人們編寫的代碼有了可讀性,有了語義,與直接用01相比,更有利於記憶。
而前面說了,計算機最終只識別二進制的指令,那麼,咱們用編程語言編寫出來的代碼就必需要轉換成供機器識別的指令。
就像這樣:

code: 1+2 function 翻譯方法(參數:code) 
{ 
    ... 
    "1"=>"001"; 
    "2"=>"002";    "+"=>"000"; 
    return 能讓機器識別的二進制代碼; 
} 
call 翻譯方法("1+2") => "001 000 002"

因此從一門編程語言所編寫的代碼文件轉換成能讓本機識別的指令,這中間是須要一個翻譯的過程。
而咱們如今計算機上是運載着操做系統的,光翻譯成機器指令也不行,還得讓代碼文件轉化成可供操做系統執行的程序才行。
那麼這些步驟,就是編程語言所對應的編譯環節的工程了。這個翻譯過程是須要工具來完成,咱們把它叫作 編譯器。

不一樣廠商的CPU有着不一樣的指令集,爲了克服面向CPU的指令集的難讀、難編、難記和易出錯的缺點,後來就出現了面向特定CPU的特定彙編語言, 好比我打上這樣的x86彙編指令 mov ax,bx ,而後用上用機器碼作的彙編器,它將會被翻譯成 1000100111011000 這樣的二進制01格式的機器指令.

不一樣CPU架構上的彙編語言指令不一樣,而爲了統一一套寫法,同時又不失彙編的表達能力,C語言就誕生了。
用C語言寫的代碼文件,會被C編譯器先轉換成對應平臺的彙編指令,再轉成機器碼,最後將這些過程當中產生的中間模塊連接成一個能夠被操做系統執行的程序。

那麼彙編語言和C語言比較,咱們就不須要去閱讀特定CPU的彙編碼,我只須要寫通用的C源碼就能夠實現程序的編寫,咱們用將更偏機器實現的彙編語言稱爲低級語言,與彙編相比,C語言就稱之爲高級語言。

在看看咱們C#,咱們在編碼的時候都不須要過於偏向特定平臺的實現,翻譯過程也基本遵循這個過程。它的編譯模型和C語言相似,都是屬於這種間接轉換的中間步驟,故而可以跨平臺。
因此就相似於C/C#等這樣的高級語言來講是不區分平臺的,而在於其背後支持的這個 翻譯原理 是否能支持其它平臺。

什麼是託管代碼,託管語言,託管模塊?

做爲一門年輕的語言,C#借鑑了許多語言的長處,與C比較,C#則更爲高級。
每每一段簡小的C#代碼,其功能卻至關於C的一大段代碼,而且用C#語言你幾乎不須要指針的使用,這也就意味着你幾乎不須要進行人爲的內存管控與安全考慮因素,也不須要多懂一些操做系統的知識,這讓編寫程序變得更加輕鬆和快捷。

若是說C#一段代碼能夠完成其它低級語言一大段任務,那麼咱們能夠說它特性豐富或者類庫豐富。而用C#編程不須要人爲內存管控是怎麼作到的呢?
    .NET提供了一個垃圾回收器(GC)來完成這部分工做,當你建立類型的時候,它會自動給你分配所須要的這部份內存空間。就至關於,有一個專門的軟件或進程,它會讀取你的代碼,而後當你執行這行代碼的時候,它幫你作了內存分配工做。 這部分本該你作的工做,它幫你作了,這就是「託管」的概念。好比現實中 託管店鋪、託管教育等這樣的別人替你完成的概念。

所以,C#被稱之爲託管語言。C#編寫的代碼也就稱之爲託管代碼,C#生成的模塊稱之爲託管模塊等。(對於託管的資源,是不須要也沒法咱們人工去幹預的,但咱們能夠了解它的一些機制原理,在後文我會簡單介紹。)

只要有比較,就會產生概念。那麼在C#角度,那些脫離了.NET提供的諸如垃圾回收器這樣的環境管制,就是對應的 非託管了。

非託管的異常

咱們編寫的程序有的模塊是由託管代碼編寫,有的模塊則調用了非託管代碼。在.NET Framework中也有一套基於此操做系統SEH的異常機制,理想的機制設定下咱們能夠直接經過catch(e)或catch來捕獲指定的異常和框架設計人員容許咱們捕獲的異常。

而異常類型的級別也有大有小,有小到能夠直接框架自己或用代碼處理的,有大到須要操做系統的異常機制來處理。.NET會對那些能讓程序崩潰的異常類型給進行標記,對於這部分異常,在.NET Framework 4.0以前容許開發人員在代碼中本身去處理,但4.0版本以後有所變動,這些被標記的異常默認不會在託管環境中拋出(即沒法catch到),而是由操做系統的SEH機制去處理。 
不過若是你仍然想在代碼中捕獲處理這樣的異常也是能夠的,你能夠對須要捕獲的方法上標記[System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptionsAttribute]特性,就能夠在該方法內經過catch捕獲到該類型的異常。你也能夠經過在配置文件中添加運行時節點來對全局進行這樣的一個配置:

HandleProcessCorruptedStateExceptions特性:https://msdn.microsoft.com/zh-cn/library/azure/system.runtime.exceptionservices.handleprocesscorruptedstateexceptionsattribute.aspx SEHException類:https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.sehexception(v=vs.100).aspx 處理損壞狀態異常博客專欄: https://msdn.microsoft.com/zh-cn/magazine/dd419661.aspx

相關文章
相關標籤/搜索