1 術(shù)語
面試出現(xiàn)頻率:從來沒人問過拦盹。事實上我都不知道怎么問,考背書嗎溪椎?倒是可以問問知不知道現(xiàn)在.NET最新版本是什么普舆,考察面試者是否對新技術(shù)足夠敏感恬口。
重要程度:3/10
需要理解的程度:知道這些縮寫(CLR,BCL沼侣,F(xiàn)CL楷兽,CTS,CLS)各代表什么即可华临。仔細讀一遍http://www.tracefact.net/CLR-and-Framework/DotNet-Framework.aspx
1.1什么是.NET框架芯杀?在各個平臺版本中,有什么值得強調(diào)的更新雅潭?
.NET框架是以一種采用系統(tǒng)虛擬機(即CLR)運行的揭厚,面向CLR的編程平臺,以CLR為基礎(chǔ)。.NET的基礎(chǔ)類庫運行于CLR之上(類比Java的虛擬機),作為其他各種功能的基石抱究。.NET框架支持多種語言(C#刑然、F#、VB.NET掌桩、C++、Python等)的開發(fā)。它的前身是Windows DNA√岵恚現(xiàn)在.NET框架的擴展性甚至超過Java,其的Mono為Mac OS提供了支持笋敞,Xamarin可媲美安卓開發(fā)碱蒙,可以在任何手機上開發(fā)。
.NET框架是開源的夯巷。它的代碼在https://github.com/dotnet/赛惩。如果你的commit有幸被接受,即使改動有多么微小趁餐,也是無上的榮耀喷兼,你絕對應該把它寫到你簡歷的第一行,這個成就可以和“為Linux內(nèi)核優(yōu)化做過貢獻”相比后雷,那可比曾經(jīng)在BAT做過幾年的經(jīng)歷牛逼多了季惯。
所有.NET支持的語言編寫出來的程序,在支持.NET的編譯器編譯之后喷面,會先產(chǎn)出程序集星瘾,其主要內(nèi)容是IL和元數(shù)據(jù)。之后惧辈,JIT再將其翻譯為機器碼琳状。
甲骨文公司的Java EE是.NET平臺的競爭對手之一。
.NET框架現(xiàn)在已經(jīng)出到了版本4.6.1盒齿。在3.0之前念逞,.NET框架的Web解決方案是ASP.NET(Webform & MVC)困食,數(shù)據(jù)庫連接為ADO.NET(支持過去的ODBC,OLE等翎承,并支持SQL Server和Oracle)硕盹,Windows Form則作為Windows下的應用解決方案。
.NET最重大的一個版本更新是3.0叨咖,其中瘩例,提出了WCF(統(tǒng)一了過去Web服務(wù)混亂的形式,形成了一個統(tǒng)一的格式甸各,并采用SOAP)垛贤,WPF(作為Windows form的增強版)以及WF。
.NET3.5集成了LINQ趣倾。另外Entity Framework取代ADO.NET聘惦,它對應VS2008。
.NET4.0提出了任務(wù)并行庫和PLINQ儒恋。
.NET 5 (即.NET Core 1.0)在2016年6月27日推出善绎。是次推出伴隨著ASP.NET Core (即ASP.NET 6)和Entity Framework 7。這些產(chǎn)品將支持Windows诫尽,OS X和Linux三種操作系統(tǒng)禀酱。
新版本的.NET項目使用.json文件代替了過去的.xxproj,.sln和.suo文件箱锐,這符合目前的主流比勉,即用json代替XML。新版本的.NET框架要傳輸給我們的理念是:這是一個跨平臺的驹止,開源的框架。一切都是依賴注入观蜗,一切都是nuget臊恋,開發(fā)徹底組件化,能解耦的全都解耦墓捻。ASP.NET Core徹底擺脫了System.Web這個頑疾抖仅,在其中,我們甚至連MVC都是注入進去的砖第。如果想得到什么組件撤卢,要么通過依賴注入,要么就使用nuget梧兼。永遠不要手動add reference放吩,目前我知道的唯一的例外是System.Configuration。當你和團隊其他人并行開發(fā)系統(tǒng)的不同模塊時羽杰,你們可以用nuget互相得到對方模塊中的工程渡紫。Nuget相比add reference到推,更不容易出錯,界面更友好惕澎,且不會輕易陷入dll陷阱莉测。
經(jīng)過.NET牌編譯器編譯之后的程序集有兩種形態(tài):類庫(.dll)形態(tài)和可執(zhí)行文件(.exe)形態(tài)。.NET自帶了很多類庫唧喉,統(tǒng)稱為FCL捣卤。BCL是FCL的一個子集。
1.2 基礎(chǔ)類庫(BCL)
Base Class Library (BCL) 是微軟所提出的一組標準庫八孝,可提供給.NET Framework所有語言使用董朝。隨著 Windows 以及.NET Framework 的成長,BCL 已近乎成為在.NET上的 Windows API唆阿。mscorlib.dll程序集幾乎就是基礎(chǔ)類庫的代名詞益涧。
當安裝.NET Framework時,所有的基礎(chǔ)類庫被部署到全局程序集緩存(GAC)驯鳖。它的位置一般在C:\Windows\assembly闲询。所以你不需要在你的工程中手動引用任何的基礎(chǔ)類庫,它們會被自動引用浅辙。如果你從GAC中刪除了mscorlib.dll扭弧,你的IDE將變成一個什么都不懂的白癡。因為沒有mscorlib.dll记舆,意味著沒有基礎(chǔ)類庫鸽捻,沒有整型,字符串泽腮,控制臺…你什么都做不了御蒲。
部分mscorlib.dll包括的命名空間:
- System:.NET Framework 類庫中最基底的服務(wù),提供應用程序域 (Application Domain)诊赊,數(shù)據(jù)類型厚满,I/O 以及其他類庫的基礎(chǔ)。
- System.Collections:提供非泛型數(shù)據(jù)結(jié)構(gòu)以及集合對象的支持碧磅,其中 System.Collections.Generic中包括所有的泛型數(shù)據(jù)結(jié)構(gòu)碘箍。
- System.Configuration:提供 .NET 應用程序在配置設(shè)置上的支持。
- System.Data:ADO.NET 的組成類庫鲸郊,為數(shù)據(jù)訪問功能的核心功能丰榴。
- System.Drawing:提供 .NET 的繪圖能力,包含基本位圖處理以及視頻與色彩處理秆撮,打印支持也由本名字空間提供四濒,此名字空間包裝了大多數(shù)的 GDI 以及 GDI+ 的 API。
- System.IO:提供數(shù)據(jù)流與文件讀寫的支持
- System.Net:.NET 中的網(wǎng)絡(luò)功能
- System.Reflection:反射
- System.Diagnostics:.NET 中提供系統(tǒng)診斷,除錯峻黍,追蹤與運行外部進程的能力
- System.ServiceModel:WCF 的組成類庫复隆,于 .NET Framework 3.0 時出現(xiàn)。
- System.Text:對文字姆涩,編碼以及正規(guī)表達式的支持挽拂。
- System.Threading:線程控制
- System.Windows.Forms: Windows Forms 的組成類庫,包裝了 Win32 用戶界面骨饿,視窗亏栈,共用控件,以及 Shell 的基礎(chǔ) API宏赘,以提供設(shè)計 Windows 應用程序用戶界面所需的支持绒北。
- System.Windows:WPF 的組成類庫,于 .NET Framework 3.0 時出現(xiàn)察署。
- System.Web:ASP.NET 的組成類庫闷游,令工程可以和 IIS 服務(wù)器交互,XML Web Service 開發(fā)的基本支持也由本類別提供贴汪。ASP.NET Core中消失(如果你不打算用IIS做服務(wù)器的容器脐往,則你不需要這個類庫)。
- System.Xml:XML 解析器
- System.Linq扳埂,System.Xml.Linq:LINQ 的核心類庫业簿,System.Linq 是 LINQ to Object,而 System.Xml.Linq 則是 LINQ to XML阳懂。
然而在C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETFramework\v4.0\我們還有一個System.dll梅尤,這個參考是每次新建工程時VS自動引用的若干參考之一。這個程序集中也有一個System命名空間岩调,它的內(nèi)容和mscorlib.dll中的不同巷燥。可以看到号枕,System這個命名空間存在于不止一個程序集中矾湃。這意味著不同的程序集可以共享一個命名空間。
在System.dll中堕澄,System類型擁有Uri這個成員,mscorlib.dll中System類型擁有int這個成員(基元類型)霉咨。所以我們可以做個試驗蛙紫,如果我們將工程中對System的引用去掉,那么我們就不能定義一個Uri類型的對象途戒。但我們?nèi)匀豢梢允褂胕nt類型坑傅,因為它雖然也在System這個類型里面,但位于mscorlib.dll中喷斋。當你去掉對System的引用時唁毒,你僅僅去掉了System.dll和里面的功能蒜茴,但你沒有去掉mscorlib.dll中System類型的功能。
BCL是屬于整個.NET框架的浆西,并非某種語言的一個基礎(chǔ)類庫粉私。例如,C#的string類型的所有功能和定義來源于mscrolib.dll中的System.String近零,而VB的string類型的功能和定義也來源于相同的地方诺核。基礎(chǔ)類庫中定義的類型稱為基元類型久信,它也是為.NET框架所有的語言共享窖杀。
在.NET Core中,BCL改名換姓變成了Corefx裙士。源碼在https://github.com/dotnet/corefx入客。
1.3 框架類庫(FCL)
作為一名.NET程序員,每天都要打交道的就是FCL了(框架類庫)腿椎。BCL是FCL的一個子集桌硫。簡單來說FCL除了BCL的那部分,就是我們要引用的外部參考酥诽。
1.4 CTS(公共類型系統(tǒng))和CLS(公共語言規(guī)范)
簡單的說鞍泉,CTS就是說話的語法和規(guī)范。你可以理解為肮帐,英語是一種語言咖驮,英語的CTS(至少絕大一部分)就是“實用英語語法(張道真)”這本書。如果C#沒了語法训枢,那就沒有class托修,沒有接口,變成了偽碼恒界。
參考資料中的第一個鏈接講的很好睦刃,我就在這里總結(jié)一下吧:
CTS是一套語法。類似“英語語法”十酣。它規(guī)定了一套約束涩拙,例如英語規(guī)定所有的字詞都是由26個字母組成的(以及其他很多規(guī)則)。服從這套語法的語言都可以被看成是英語的某種方言耸采,例如中古英語兴泥,現(xiàn)代英語都是英語,而漢語不符合字詞由字母組成虾宇,所以它不是英語搓彻。同理所有服從CTS的語言,都可以被看成.NET框架的語言。
CTS中定義了類型旭贬,允許它有屬性怔接,字段,方法等稀轨。
.NET框架的眾多語言各自實現(xiàn)了CTS的一部分功能扼脐。做一個不太恰當?shù)念惐龋珻#可以被認為是“美國英語”靶端,F(xiàn)#是“英國英語”而VB是“印度英語”等谎势。他們是英語的各種方言。他們共享一套相同的詞匯表杨名,但也各有各的特點脏榆。例如顏色在英國英語中的拼寫是colour,美國英語則是color台谍。
由于.NET框架的眾多語言在編譯時都要轉(zhuǎn)換為IL须喂,因此IL實現(xiàn)的CTS功能是它們的并集,也就是CTS全部的功能趁蕊。你可以理解為坞生,雖然.NET框架語言那么多,但一編譯了之后掷伙,就成了一種語言是己。
.NET框架的眾多語言分享CTS的一小部分功能,這部分功能稱為CLS(Common Language Specification任柜,公共語言規(guī)范)卒废。這是這些語言(的程序集)可以相互使用的前提。如果你創(chuàng)建一個新語言宙地,其實現(xiàn)了CTS的一部分功能摔认,但不包括CLS,那你的語言就不能被其他.NET框架的語言(的程序集)使用宅粥。如果你創(chuàng)建的語言甚至不符合CTS参袱,例如你在詞匯表中加入了漢字,那不好意思秽梅,你創(chuàng)建的語言不能叫英語抹蚀。
很明顯,CLS是CTS的一個子集企垦,而且是最小的子集况鸣。(最小功能集)
圖片來自CLR via C#。
1.5 為什么說.NET是平臺無關(guān)的竹观?
.NET程序集可以在非微軟操作系統(tǒng)如Mac OS,各種版本的Linux,以及iOS和Android移動設(shè)備上開發(fā)和執(zhí)行臭增。.NET的平臺無關(guān)性主要體現(xiàn)為:.NET程序集可以在任何的平臺上運行懂酱,不管是Windows,還是Mac誊抛,只要這個平臺擁有將IL轉(zhuǎn)換為機器碼列牺,以及加載其他相關(guān)程序集的能力(即CLR),而任何機器都可以運行機器碼拗窃。這類似于Java的虛擬機瞎领,只要平臺裝了Java虛擬機,則這個平臺就可以運行Java程序随夸。
1.6 CLR(公共語言運行時)
CLR是讓程序執(zhí)行所需的外部服務(wù)的集合九默,類似Java需要JVM虛擬機才可以運行。
它的核心功能(比如即時編譯宾毒,內(nèi)存管理驼修,程序集加載,安全性诈铛,異常處理和線程同步)可由面向CLR的所有語言使用乙各。例如,CLR允許創(chuàng)建線程幢竹,所以面向CLR的所有語言都能創(chuàng)建線程耳峦。
CLR是.NET的運行基礎(chǔ),管理.NET程序集的執(zhí)行焕毫。它運行于Windows之上蹲坷,很多功能僅僅是Windows上的一個wrapper,例如線程咬荷,內(nèi)存管理等冠句,這些實際上是Windows在管理。但JIT則是它獨有的幸乒,如果沒有它懦底,就不能把IL變成機器碼,計算機也就不認識C#罕扎,你也就不能運行C#程序聚唐。
在開始運行.NET程序之前,編譯器將代碼轉(zhuǎn)換為IL腔召。IL代碼并不能直接運行杆查,CLR將真正需要用到的程序集導入內(nèi)存,讀取元數(shù)據(jù)臀蛛,接著為類型開辟內(nèi)存空間亲桦,執(zhí)行所有需要的安全檢查崖蜜,并最終運行代碼:
CLR找到代碼中擁有Main方法的類型并且加載這個類型。CLR中一個名為Class loader(類加載程序)的組件負責這項工作客峭。它會從GAC豫领、配置文件、程序集元數(shù)據(jù)中尋找這個類型舔琅,然后將它的類型信息加載到內(nèi)存中的數(shù)據(jù)結(jié)構(gòu)中等恐。在Class loader找到并加載完這個類型之后,它的類型信息會被緩存起來备蚓,這樣就無需再次進行相同的過程课蔬。當然,如果這個類型引用了其他的類型郊尝,則會導致一連串的程序集加載二跋,這將定義程序代碼執(zhí)行的環(huán)境(類似Java的JVM)。注意即使工程很大虚循,有幾百個程序集同欠,CLR不會全部加載,只會在真正用到該程序集的時候才加載横缔。
驗證铺遂。在CLR中,還存在一個驗證程序(verifier)茎刚,該驗證程序的工作是在運行時確保代碼是類型安全的襟锐。它主要校驗兩個方面,一個是元數(shù)據(jù)是正確的膛锭,一個是IL代碼必須是類型安全的粮坞,類型的簽名必須正確。這是早期綁定驗證初狰,驗證在運行時之前發(fā)生莫杈。對于動態(tài)類型,此時不做任何檢查奢入。
即時編譯筝闹。(此時就從編譯時過渡到了運行時)這一步就是將托管的IL代碼編譯為可以執(zhí)行的機器代碼的過程,由CLR的即時編譯器(JIT Complier)完成腥光。即時編譯只有在方法的第一次調(diào)用時發(fā)生关顷。類型加載程序(Class loader)會為每個方法插入一個存根。在調(diào)用方法時武福,CLR會檢查方法的存根议双,如果存根為空,則執(zhí)行JIT編譯過程捉片,并將該方法被編譯后的本地機器代碼地址寫入到方法存根中平痰。當?shù)诙螌ν环椒ㄟM行調(diào)用時汞舱,會再次檢查這個存根,如果發(fā)現(xiàn)其保存了本地機器代碼的地址觉增,則直接跳轉(zhuǎn)到本地機器代碼進行執(zhí)行兵拢,無需再次進行JIT編譯。JIT編譯還會優(yōu)化本地的代碼逾礁。
在程序運行時,CLR還負責:
- 異常處理
- 內(nèi)存管理與垃圾回收
- 線程管理(線程池)
托管代碼是必須在CLR下執(zhí)行的代碼访惜,而非托管代碼則不需要CLR的支持就可以運行嘹履。CLR本身用于管理托管代碼,因此它是由非托管代碼編寫的债热,并不是一個包含了托管代碼的程序集砾嫉,也不能使用IL DASM進行查看。它位于C:%SystemRoot%\Microsoft.NET\Framework\版本號下窒篱,視安裝的機器不同有兩個版本焕刮,一個是工作站版本的mscorwks.dll,一個是服務(wù)器版本的mscorsvr.dll墙杯。wks和svr分別代表workstation和server配并。
CLR via C#這本書選擇通過C#作為視角,討論CLR的各種功能高镐。通過對這本書的閱讀溉旋,你會對一些實際由CLR進行管理的行為例如垃圾回收,線程管理有更加深刻的認識嫉髓。
2. 編譯:IL與JIT
面試出現(xiàn)頻率:低观腊。不排除部分IL專家會試探性問你一些IL命令,但我相信你答不出來他們也不會在意算行。學了IL和沒學梧油,一般人看不出來區(qū)別,學了IL州邢,也不意味著你就很厲害儡陨。個人認為,學IL唯一的用處就在于證明你看到的書上寫的各種結(jié)論偷霉,或者驗證一些性能方面的想法迄委。你可以參看這篇文章:http://blog.zhaojie.me/2009/06/my-view-of-il-2-il-shows-little-about-clr.html
重要程度:3/10,常識性了解即可
需要理解的程度:知道IL是中間代碼类少,知道JIT的優(yōu)點(帶緩存的編譯)叙身,以及它可能會對你的代碼進行優(yōu)化。
2.1 什么是IL(CIL)硫狞?如何獲得IL代碼信轿?
在.NET的開發(fā)過程中晃痴, IL的官方術(shù)語是MSIL或CIL(Common Intermediate Language,即公共中間語言)财忽。因此,IL即彪,MSIL和CIL指的是同一種東西漏益。
當使用支持.NET的編譯器編譯之后,生成.dll或.exe文件。這文件稱作.NET程序集余爆,包含IL和元數(shù)據(jù)。不同語言(例如C#和VB)經(jīng)過不同編譯器(例如C#編譯器和VB編譯器),編譯一段功能相似的代碼(區(qū)別僅僅在于語法)五芝,其IL也基本相似。雖然IL相對C#較為底層醉途,但它仍然是一個十分高級的語言凉夯。它并不是匯編語言震桶。
可以通過ildasm(在cmd中運行)工具加載任意的.NET程序集并分析它的內(nèi)容磨取,包括它所包含的IL代碼和元數(shù)據(jù)。注意隘截,高級語言只公開了CLR的所有功能的一個子集东臀,而IL允許開發(fā)人員訪問CLR所有的功能。
關(guān)于IL的擴展閱讀赁濒,可參看老趙談IL系列:http://blog.zhaojie.me/2009/06/my-view-of-il-1-il-and-asm.html
2.2 什么是JIT?還有什么其他編譯方式?何時使用到JIT丁侄?
即時編譯(英語:Just-in-time compilation)是動態(tài)編譯的一種形式捍歪,是一種提高程序運行效率的方法庐镐。通常,程序有兩種運行方式:靜態(tài)編譯與動態(tài)編譯粟矿。靜態(tài)編譯的程序在執(zhí)行前全部被翻譯為機器碼陌粹,而動態(tài)編譯執(zhí)行的則是一句句荆姆,邊運行邊翻譯仆救。
即時編譯則混合了這二者构诚,一句句編譯源代碼,但是會將翻譯過的代碼緩存起來以降低性能損耗铆惑。相對于靜態(tài)編譯代碼范嘱,即時編譯的代碼可以處理延遲綁定并增強安全性。
CLR的JIT負責將IL編譯成機器碼员魏。 當程序編譯成程序集之后丑蛤,CLR加載任何需要用到的其他程序集,并開始使用JIT將CIL編譯為機器碼撕阎。JIT編譯器會在方法的首次調(diào)用時受裹,從類型的元數(shù)據(jù)中查找方法,并進行檢查,例如檢查類型是否安全棉饶。如果出現(xiàn)了問題厦章,則觸發(fā)運行時錯誤。以后對方法的所有調(diào)用都以本地代碼的形式全速運行照藻,無須重新檢查袜啃。
2.3 本地代碼的優(yōu)化
CLR的JIT編譯器會對本地代碼進行優(yōu)化。例如字符串駐留中對常量字符串相加的優(yōu)化幸缕。和沒有優(yōu)化相比群发,優(yōu)化之后的代碼將獲得更出色的性能。但過度的優(yōu)化可能會出現(xiàn)問題发乔,在CLR via C#的易失構(gòu)造中熟妓,作者舉了一個例子。
class Program
{
private static bool s_stopWorker = false;
static void Main()
{
Console.WriteLine("Main: letting worker run for 2 seconds");
Thread t = new Thread(Worker);
t.Start();
Thread.Sleep(2000);
s_stopWorker = true;
Console.WriteLine("Main: waiting for worker to stop");
t.Join();
}
private static void Worker(object o)
{
int x = 0;
while (!s_stopWorker)
{
x++;
}
Console.WriteLine("Worker: stopped when x = {0}", x);
}
}
如果使用f5呼叫出Visual Studio的調(diào)試模式栏尚,則程序會像預期的那樣正常運行直到結(jié)束起愈。使用調(diào)試器會造成JIT編譯器在Debug模式進行編譯,它生成未優(yōu)化的代碼译仗,目的是方便你進行單步調(diào)試告材。如果是選擇了x86的Release模式進行編譯:
它將會生成被CLR優(yōu)化的代碼。值得一提的是古劲,x86編譯器是一個更成熟的編譯器,執(zhí)行優(yōu)化比x64更大膽缰猴。x64不會執(zhí)行上面所說的特定的優(yōu)化产艾。在再次用f6進行編譯之后,用ctrl+f5運行程序滑绒,程序?qū)萑霟o限循環(huán)闷堡。
注意:必須用x86+Release編譯,然后以非調(diào)試模式運行(即Ctrl+F5)疑故,才能看到這個效果杠览。問題發(fā)生的原因是,x86的編譯優(yōu)化過度纵势。它發(fā)現(xiàn)變量s_stopWorker要么為true要么為false踱阿。它還發(fā)現(xiàn)這個值在worker方法本身中從來沒有變化。因此钦铁,編譯器會生成代碼檢查s_stopWorker软舌,如果s_stopWorker為true,就顯示“Worker: stopped when x = 0”牛曹。如果s_stopWorker為false編譯器就生成代碼進入一個無限循環(huán)佛点,并一直遞增x。解決的辦法是為s_stopWorker加入修飾詞volatile。
PDB文件包含了可以令調(diào)試器在本地工作的信息超营≡妫可以這么說:有了PDB文件,本地的debug才成為可能演闭。如果你打算發(fā)布Release版本不跟,則不需要該文件。使用Release模式編譯的結(jié)果中也不包含PDB文件船响。例如躬拢,你寫了一個小的控制臺程序給別人用,那么你不需要把\bin\debug里面所有的文件都拷貝給別人见间,你只需要程序本身聊闯,必要的dll和config文件即可。