文章出處

微軟推出的第一個版本的.NET Framework是一個面向Windows桌面和服務器的基礎框架,在此之后,為此微軟根據設備自身的需求對.NET Framework進行裁剪,不斷推出了針對具體設備類型的.NET Framework版本以實現針對移動、平板和嵌入式設備提供支持。除此之外,在Windows平臺之外一致游蕩著一只特立獨行的猴子(Mono)。.NET平臺看起來欣欣向榮,而實際上卻日薄西山,就在這個時候微軟走了一條唯一正確的道路,那就是基于跨平臺理念重新設計的.NET Core,以及由此驅動地對整個.NET平臺進行全新布局。

.NET Core跨平臺的奧秘[上篇]:歷史的枷鎖
.NET Core跨平臺的奧秘[中篇]:復用之殤
.NET Core跨平臺的奧秘[下篇]:全新的布局

對于計算機從業人員來說,“平臺(Platform)”是一個我們司空見慣的詞語,在不同的語境中它具有不同的語義,比如它可以指代操作系統環境和CPU架構類型,也可以表示硬件設備類型。經過多年的苦心經營,微軟已經為在Windows平臺下構建了一個完整的支持多種設備的.NET生態系統。與此同時,通過借助于Mono和Xamarin,.NET已經可以被成功移植到包括Mac OS X、Linux、iOS、Android和FreeBSD等非Windows平臺。

一、Windows下的.NET

微軟在2002年推出了第一個版本的 .NET Framework,這是一個主要面向Windows 桌面(Windows Forms)和服務器(ASP.NET Web Forms)的基礎框架。在此之后,PC的霸主地位不斷受到其他設備的挑戰甚至取代,為此微軟根據設備自身的需求對.NET Framework作了相應的簡化和改變,不斷推出了針對具體設備類型的.NET Framework,主流的包括Windows Phone、Windows Store、Silverlight和.NET Micro Framework等,它們分別對移動、平板和嵌入式設備提供支持。由于這些不同的.NET Framework分支是完全獨立的,這使我們很難開發一個支持多種設備的“可移植(Portable)”應用。

.NET Framework的層次結構

針對不同設備.NET Framework的獨立性導致了在很多情況下我們不得不針對具體的設備平臺進行編程,跨設備平臺代碼的重用顯得異常困難。為了讓讀者朋友們對這個問題具有深刻地理解,我們從.NET Framework的結構開始講起。從結構組成的角度來講,.NET Framework由如下圖所示的兩個層析構成,它們分別是提供運行環境的CLR(Common Language Runtime)和提供API的FCL(Framework Class Library)。

2-1

CLR之于.NET等同于JVM之于Java,它是.NET虛擬機。作為一個運行時(Runtime),CLR為程序的執行提供一個托管(Managed)的執行環境,它是.NET Framework的執行引擎,為托管程序的執行提供內存分配、垃圾回收、安全控制、異常處理和多線程管理等方面的服務。CLR是.NET Framework的子集,但是兩者卻具有不同的版本策略。到目前為止,微軟僅僅發布了4個版本的CLR,它們分別是1.0、1.1、2.0和4.0,.NET Framework 1.0和1.1分別采用CLR 1.0和1.1,CLR 2.0被.NET Framework 2.0和3.x共享,.NET Framework 4.x下的運行時均為CLR 4.0。

FCL是一個旨在為開發人員提供API的類庫,由它提供的API又可以劃分為如上圖所示的兩個層次。處于最底層的部分被稱為BCL(Basic Class Library),它提供了一系列基礎類型,它們用于描述一些基本的數據類型和數據結構(比如字符串、數字、日期/時間和集合等)和提供一些基礎性的操作(比如IO、診斷、反射、文本編碼、安全控制、多線程管理等)。在BCL之上的則是面向具體應用類型的API,我們大體上可以將它們劃分為入下三種類型:

  • 面向應用(比如ASP.NET、WPF和Windows Forms等)
  • 面向服務(比如WCF、WF和Data Services等)
  • 面向數據(比如ADO.NET、Entity Framework和LinQ to SQL等)

我們也可以采用另一種方式對FCL進行重新劃分:將面向某種應用或者服務類型(比如Windows Forms、WPF、ASP.NET和WCF等)的部分成為AppModel,那么整個.NET Framework則具有了如下圖所示的三層結構。

2-2

大而全的BCL

我們知道微軟的.NET戰略是在千禧年提出來的,兩年之后第一個.NET Framework版本和IDE(VS.NET 2002)隨之問世。在之后的10多年中,一系列版本的.NET Framework被先后推出。微軟目前發布的最新.NET Framework版本為4.7,下圖為你展示了整個.NET Framework不斷升級的演進過程,以及各個版本提供的主要特性。

2-3

上圖勾勒出.NET Framework這些年的發展歷程旨在說明一個問題:作為整個.NET平臺的基礎框架,.NET Framework在不斷升級過程中是自己變得更加強大和完備,但是在另一方面也是自己變得越來越臃腫。隨著版本的不斷升級,構成.NET Framework的應用模型、BCL和運行時(CLR)都在不斷地膨脹(.NET Framework 2.0/3.x和.NET Framework 4.x分別采用CLR 2.0和CLR 4.0),下圖很直觀地說明了這個問題。

2-4

我們知道程序集是.NET最基本的部署單元,不論定義其中的多少類型被使用,CLR總是將整個程序集加載到內存中。對于上面介紹的構成.NET Framework的三個層次來說,應用模型是針對具體應用/服務類型的,相應的API通過獨立的程序集來承載(比如ASP.NET的核心框架定義在程序集System.Web.dll中,承載整個Windows Forms框架的程序集則是System.Windows.Forms.dll),所以.NET Framework的各個應用模型是相互獨立的。在開發某種類型的應用時,我們只需要引用應用模型對應的程序集就可以了,也就是說我們開發一個Windows Forms應用,是不需要去引用System.Web.dll程序集的。

但是BCL的絕大部分核心代碼都定義在mscorlib.dll這個核心程序集中,所以BCL基本上來說是作為一個不可分割的整體存在于.NET Framework之中。.NET Framework需要對運行在本機各種類型的托管程序提供支持,針對所有應用類型的基礎類型均需要定義在BCL中。在很多情況下,我們的應用可能僅僅需要使用到BCL一個很小的子集,但是我們不得不將定義整個程序集都加載到內存之中。

一方面BCL總是作為一個不可分割的整體被加載,另一方面其自身的尺寸也在隨著.NET Framework的升級而不斷地膨脹。對于客戶端應用(比如Windows Forms/WPF應用)來說,這應該不算是一個大不了的問題,但是對于移動和服務端應用(包括部署于云端應用)來說,由此帶來的對性能和吞吐量的響應就成了一個不得不考慮的問題。

理想的BCL消費方式是“按需消費”,我們需要那個部分就加載那個部分。由于作為獨立部署單元的程序集總是作為一個整體被CLR加載到內存中,要完全實現這種理想的BCL消費方式,唯一的辦法就是將其劃分為若干小的單元,并分別定義到獨立的程序集中。除此之外,按照模塊化的原則對整個BCL進行拆分也是版本升級變得更加容易,如果現有版本具有需要修復的Bug,或者性能需要改進,那么只需要改動并升級相應的模塊就可以了。下圖展示了具有模塊化BCL的.NET Framework層級結構。

2-5

多個設備平臺獨自為政

經過多年的經營,微軟已經為我們構建了一個完整的支持多種設備的.NET生態系統,從最初單純的桌面平臺,逐漸擴展到移動、平板和嵌入式等平臺。設備運行環境的差異性導致了針對它們的應用不能構建在一個統一的.NET Framework平臺上,所以微軟采用獨立的.NET Framework平臺來對它們提供針對性的支持。就目前來說,除了支持Windows 桌面和服務器設備的“完整版 .NET Framework”之外,微軟還先后推出了一系列“壓縮版.NET Framework”,這其中就包括Windows Phone、Windows Store、Silverlight.NET Micro Framework等,它們分別對移動、平板和嵌入式設備提供支持。

這些.NET Framework并不是僅僅在AppModel層次提供針對相應設備平臺的開發框架,它們提供的BCL和Runtime也是不同。換句話說,這些.NET Framework平臺是完全獨立的,不同.NET Framework平臺之間的獨立性很直觀地體現在下圖之中。目標平臺的獨立性導致我們很難編寫能夠在各個平臺復用的代碼,關于這一點我們會在下面一節“復用之傷”中做重點討論。

2-6

二、非Windows下的.NET

盡管微軟自身多年以來基本上都只在Windows平臺下的一畝三分地上進行耕耘,但是.NET 則通過Mono和Xamarin將觸角延伸到其他平臺(Mac OS X、Linux、iOS和Android等)。雖然目前做得并不算完美,但是我們可以說.NET具備跨平臺的能力。

從CLI談起

.NET跨平臺的能力建立在一種開放的標準或者規范之上,這個所謂的標準/規范就是CLI。CLI的制定旨在解決這樣一個問題:由不同(高級)編程語言開發的.NET應用能夠在無需任何更改的情況下運行于不同的系統環境下。要實現這個目標,必需有效地解決這里涉及到兩種類型的差異,即編程語言的差異運行時環境的差異。編程語言之間能夠實現相互兼容、運行時環境能夠得到統一,跨平臺的偉業方能實現。

CLI全稱為Common Language Infrastructure,其中Common Language說的是語言,具體來說是一種通用語言,它旨在解決各種高級開發語言的兼容性問題。Infrastructure指的則是運行時環境,旨在彌合不同平臺之間執行方式的差異。Common Language是對承載應用的二進制內容的靜態描述,Infrastructure則表示動態執行應用的引擎,所以CLI為可執行代碼和執行引擎確立一個統一的標準。

編程語言有編譯型和解釋型之別,前者需要通過編譯器進行編譯以生成可執行代碼,CLI涉及的Common Language指的是編譯型語言。要實現真正的跨平臺,最終需要解決的是可執行代碼在不同平臺之間的兼容和可移植的問題,而編程語言的選擇僅僅決定了應用源文件的原始狀態,應用的兼容性和可移植性由編譯后的結果來決定。如果通過不同編程語言開發的應用通過相應的編譯器編譯后能夠生成標準的目標代碼,那么編程語言之間的差異就不再是一個問題了。

按照CLI的規定,用來描述可執行代碼的是一種叫做CIL(Common Intermediate Language)的語言,這是一種介于高級語言和機器語言之間的中間語言。如下圖所示,雖然程序源文件由不同的編程語言編寫,但是我們可以借助相應的編譯器將其編譯成CIL代碼。原則上講,我們可以設計出新的編程語言并將其加入到.NET大家庭中,只需配以相應的編譯器生成統一的CIL代碼即可。我們也可以為現有的某個編程語言設計一種以CIL為目標語言的編譯器使之成為.NET語言。CIL是一門中間語言,同時也是一門面向對象的語言,所以對于一個CIL程序來說,類型是基本的組成單元和核心要素。微軟制定了一個名為CTS(Common Type System)的規范為CLI確立了一個統一的類型系統。

2-7

編程語言的差異通過編譯器這個適配器得以“同一化”,運行環境的差異則可以通過虛擬機(VM:Virtual Machine)技術來解決。虛擬機是CIL的執行容器,它能夠在執行CIL代碼的過程中采用及時編譯的方式將它動態地翻譯成與當前執行環境完全匹配的機器指令。虛擬機屏蔽了不同操作系統之間的差異,讓目標程序可以不做任何修改的情況下就能運行于不同的底層執行環境中,而CIL實際上是一種虛擬機語言。

2-8

從實現原理來看,讓.NET能夠跨平臺其實不難,但是讓各種相關的人員參與進行以構建一個健康而完善的跨平臺.NET生態圈則注定不是一件一蹴而就的事情,這里涉及的利益相關方包括編程語言的設計者,以及設計和開發編譯器、虛擬機、IDE以及其他相關工具的人,當然還包括廣大的應用開發者。跨平臺.NET生態環境必須建立在一個標準的規范之上,所以微軟為此制定了CLI,然后提交給歐洲計算機制造商協會(ECMA:European Computer Manufacturers Association)并被后者接受,成為了一個編號為335的規范,所以CLI又被稱為ECMA-335(順便說一下,ECMA還接受了微軟為C#這們編程語言制定的規范,即ECMA-334)。

Mono與Xamarin

CLI(ECMA-335)這一開放的規范在.NET誕生的那一刻起就賦予了它跨平臺的基因,但是被烙上Windows這一印記的微軟似乎根本就不曾想過將.NET推廣到其他的平臺,真正完成這一使命了是一個叫做Mono的項目。雖然Mono已經是一個不算年輕的項目了,但是依然有很多人對它不是很了解,所以我們不妨來簡單介紹一下它的歷史。

1999年,Miguel de Icaza創建了一家叫做Ximian的公司,這是一家旨在為GNOME項目(這是一個為類Unix系統提供桌面環境的GNU項目,GNOME是目前Linux最常用的桌面環境之一)開發軟件和提供支持的公司。2000年6月,微軟正式發布.NET Framework,Miguel de Icaza被個“基于互聯網的全新開發平臺”(.NET在發布的時候被標榜為“a new platform based on Internet standards”)深深吸引。同年11月,微軟發布了CLI規范(ECMA-335)并為公眾開放了獨立實現的許可,Miguel de Icaza從中看到了商機,因為這實際上為.NET走向非Windows平臺提供了可能。Miguel de Icaza在2001年7月開啟了Mono這個項目,并采用C#作為主要的開發語言(目前支持VB .NET),所以針對CLI和C#的兩個ECMA規范是構建Mono項目的理論基礎,如果訪問Mono的官方網站,我們會發現它是這樣定義Mono的:“Mono is an open source implementation of Microsoft's .NET Framework based on the ECMA standards for C# and the Common Language Runtime.”

Mono的使命不僅僅局限于能夠將.NET應用正常運行在其他非Windows平臺,它還希望幫助開發人員能夠直接在其他平臺進行. NET應用的開發,所以Mono不僅僅根據CLI為相應的平臺開發了作為虛擬機的CLR和編譯器,還提供給了IDE和相應的開發工具(被稱為MonoDevelop)。Mono的第一個正式版本(Mono 1.0)在項目開啟差不多三年之后(2004年6月)發布。

2003年8月,Ximian被另一家叫做Novell的公司收購,后者繼續支持Miguel de Icaza開發Mono項目,在這期間Mono陸續推出了若干Mono 2.x版本。2011年4月,Novell又被另一件叫做Attachmate的公司收購,后者決定放棄Mono,于是Miguel de Icaza帶著整個Mono團隊成立了一個家新的公司,起名為Xamarin。同年7月,Xamarin向原來的母公司Novell拿到了Mono的開發許可。在此之后的幾年內,Xamarin先后發布了Mono 3.x、Mono 4.0和Mono 5.x,目前的最新版本為5.4。Mono現今的目標是實現.NET 4.5除WPF、WF和部分WCF外的所有特性,目前缺失的部分的開發正在通過一個叫做Olive(Mono的一個子項目)的項目進行著。

在Mono項目的基礎之上,Xamarin開始開發以新公司命名的產品,其中最重要版本當屬2013年2月發布的Xamarin 2.0。Xamarin 2.0由Xamarin.Android、Xamarin.iOS和Xamarin.Windows組成,它們使我們可以采用C#開發針對Android、iOS和Windows的Native應用。除此之外,Xamarin 2.0還攜帶著一個叫做Xamarin Studio(MonoDevelop的升級版)的IDE以及與一些與Visual Studio集成的工具。2014年5月Xamarin 3.0發布,作為其核心的Xamarin.Forms為不同平臺的Native應用提供統一的控件,也就是說我們利用Xamarin.Forms API開發Native應用可以在無需做任何改變的情況下運行在Android、iOS和Windows上。

2016年2月,微軟和Xamarin宣布雙方簽署協議達成了前者針對后者的收購。在2016年Build大會上,微軟宣布將整個Xamarin SDK開源,并將它作為一個免費的工具集成到Visual Studio中,Visual Studio企業版的用戶還可以免費使用Xamarin企業版的所有特性。

 

綜上所述,由于.NET是建立在CLI這一標準的規范之上,所以它天生就具有了“跨平臺”的基因。在微軟發布了第一個針對桌面和服務器平臺的.NET Framework之后,它開始 “樂此不疲” 地對這個完整版的.NET Framework進行不同范圍和層次的 “閹割” ,進而造就了像Windows Phone、Windows Store、Silverlight和.NET Micro Framework的壓縮版的.NET Framework。從這個意義上講,Mono和它們并沒有本質的區別,唯一不同的是Mono真正突破了Windows平臺的藩籬。包括Mono在內的這些分支促成了.NET的繁榮,但我們都知道這僅僅是一種虛假的繁榮而已。雖然都是.NET Framework的子集,但是由于它們采用完全獨立的運行時和基礎類庫,這使我們很難開發一個支持多種設備的“可移植(Portable)”應用,這些分支反而成為制約.NET發展的一道道枷鎖。至于為什么“可移植(Portable)”.NET應用的開發如此繁瑣,敬請關注中篇《.NET Core跨平臺的奧秘[中篇]:復用之殤》。


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()