.NET面试题系列[1] - .NET框架基础知识(1)

很明显,CLS是CTS的一个子集,并且是最小的子集。 - 张子阳

.NET框架基础知识(1)

参考资料:html

1 术语

面试出现频率:历来没人问过。事实上我都不知道怎么问,考背书吗?却是能够问问知不知道如今.NET最新版本是什么,考察面试者是否对新技术足够敏感。git

重要程度:3/10程序员

须要理解的程度:知道这些缩写(CLR,BCL,FCL,CTS,CLS)各表明什么便可。仔细读一遍http://www.tracefact.net/CLR-and-Framework/DotNet-Framework.aspxgithub

 

1.1什么是.NET框架?在各个平台版本中,有什么值得强调的更新?

.NET框架是以一种采用系统虚拟机(即CLR)运行的,面向CLR的编程平台,以CLR为基础。.NET的基础类库运行于CLR之上(类比Java的虚拟机),做为其余各类功能的基石。.NET框架支持多种语言(C#、F#、VB.NET、C++、Python等)的开发。它的前身是Windows DNA。如今.NET框架的扩展性甚至超过Java,其的Mono为Mac OS提供了支持,Xamarin可媲美安卓开发,能够在任何手机上开发。面试

.NET框架是开源的。它的代码在https://github.com/dotnet/若是你的commit有幸被接受,即便改动有多么微小,也是无上的荣耀,你绝对应该把它写到你简历的第一行,这个成就能够和“为Linux内核优化作过贡献”相比,那可比曾经在BAT作过几年的经历牛逼多了。数据库

全部.NET支持的语言编写出来的程序,在支持.NET的编译器编译以后,会先产出程序集,其主要内容是IL和元数据。以后,JIT再将其翻译为机器码。编程

 

                       

 

甲骨文公司的Java EE是.NET平台的竞争对手之一。json

.NET框架如今已经出到了版本4.6.1。在3.0以前,.NET框架的Web解决方案是ASP.NET(Webform & MVC),数据库链接为ADO.NET(支持过去的ODBC,OLE等,并支持SQL Server和Oracle),Windows Form则做为Windows下的应用解决方案。缓存

.NET最重大的一个版本更新是3.0,其中,提出了WCF(统一了过去Web服务混乱的形式,造成了一个统一的格式,并采用SOAP),WPF(做为Windows form的加强版)以及WF。安全

.NET3.5集成了LINQ。另外Entity Framework取代ADO.NET,它对应VS2008。

.NET4.0提出了任务并行库和PLINQ。

.NET 5 (即.NET Core 1.0)在2016年6月27日推出。是次推出伴随着ASP.NET Core (即ASP.NET 6)和Entity Framework 7。这些产品将支持Windows,OS X和Linux三种操做系统。

新版本的.NET项目使用.json文件代替了过去的.xxproj,.sln和.suo文件,这符合目前的主流,即用json代替XML。新版本的.NET框架要传输给咱们的理念是:这是一个跨平台的,开源的框架。一切都是依赖注入,一切都是nuget,开发完全组件化,能解耦的全都解耦。ASP.NET Core完全摆脱了System.Web这个顽疾,在其中,咱们甚至连MVC都是注入进去的。若是想获得什么组件,要么经过依赖注入,要么就使用nuget。永远不要手动add reference,目前我知道的惟一的例外是System.Configuration。当你和团队其余人并行开发系统的不一样模块时,大家能够用nuget互相获得对方模块中的工程。Nuget相比add reference,更不容易出错,界面更友好,且不会轻易陷入dll陷阱。

通过.NET牌编译器编译以后的程序集有两种形态:类库(.dll)形态和可执行文件(.exe)形态。.NET自带了不少类库,统称为FCL。BCL是FCL的一个子集。

 

1.2 基础类库(BCL)

Base Class Library (BCL) 是微软所提出的一组标准库,可提供给.NET Framework全部语言使用。随着 Windows 以及.NET Framework 的成长,BCL 已近乎成为在.NET上的 Windows API。mscorlib.dll程序集几乎就是基础类库的代名词。

当安装.NET Framework时,全部的基础类库被部署到全局程序集缓存(GAC)。它的位置通常在C:\Windows\assembly。因此你不须要在你的工程中手动引用任何的基础类库,它们会被自动引用。若是你从GAC中删除了mscorlib.dll,你的IDE将变成一个什么都不懂的白痴。由于没有mscorlib.dll,意味着没有基础类库,没有整型,字符串,控制台…你什么都作不了。

部分mscorlib.dll包括的命名空间:

  • System:.NET Framework 类库中最基底的服务,提供应用程序域 (Application Domain),数据类型,I/O 以及其余类库的基础。
  • System.Collections:提供非泛型数据结构以及集合对象的支持,其中 System.Collections.Generic中包括全部的泛型数据结构。
  • System.Configuration:提供 .NET 应用程序在配置设置上的支持。
  • System.Data:ADO.NET 的组成类库,为数据访问功能的核心功能。
  • System.Drawing:提供 .NET 的绘图能力,包含基本位图处理以及视频与色彩处理,打印支持也由本名字空间提供,此名字空间包装了大多数的 GDI 以及 GDI+ 的 API。
  • System.IO:提供数据流与文件读写的支持
  • System.Net:.NET 中的网络功能
  • System.Reflection:反射
  • System.Diagnostics:.NET 中提供系统诊断,除错,追踪与运行外部进程的能力
  • System.ServiceModel:WCF 的组成类库,于 .NET Framework 3.0 时出现。
  • System.Text:对文字,编码以及正规表达式的支持。
  • System.Threading:线程控制
  • System.Windows.Forms: Windows Forms 的组成类库,包装了 Win32 用户界面,视窗,共用控件,以及 Shell 的基础 API,以提供设计 Windows 应用程序用户界面所需的支持。
  • System.Windows:WPF 的组成类库,于 .NET Framework 3.0 时出现。
  • System.Web:ASP.NET 的组成类库,令工程能够和 IIS 服务器交互,XML Web Service 开发的基本支持也由本类别提供。ASP.NET Core中消失(若是你不打算用IIS作服务器的容器,则你不须要这个类库)。
  • 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命名空间,它的内容和mscorlib.dll中的不一样。能够看到,System这个命名空间存在于不止一个程序集中。这意味着不一样的程序集能够共享一个命名空间。

在System.dll中,System类型拥有Uri这个成员,mscorlib.dll中System类型拥有int这个成员(基元类型)。因此咱们能够作个试验,若是咱们将工程中对System的引用去掉,那么咱们就不能定义一个Uri类型的对象。但咱们仍然可使用int类型,由于它虽然也在System这个类型里面,但位于mscorlib.dll中。当你去掉对System的引用时,你仅仅去掉了System.dll和里面的功能,但你没有去掉mscorlib.dll中System类型的功能。

BCL是属于整个.NET框架的,并不是某种语言的一个基础类库。例如,C#的string类型的全部功能和定义来源于mscrolib.dll中的System.String,而VB的string类型的功能和定义也来源于相同的地方。基础类库中定义的类型称为基元类型,它也是为.NET框架全部的语言共享。

在.NET Core中,BCL更名换姓变成了Corefx。源码在https://github.com/dotnet/corefx

1.3 框架类库(FCL)

做为一名.NET程序员,天天都要打交道的就是FCL了(框架类库)。BCL是FCL的一个子集。简单来讲FCL除了BCL的那部分,就是咱们要引用的外部参考。

 

1.4 CTS(公共类型系统)和CLS(公共语言规范)

简单的说,CTS就是说话的语法和规范。你能够理解为,英语是一种语言,英语的CTS(至少绝大一部分)就是“实用英语语法(张道真)”这本书。若是C#没了语法,那就没有class,没有接口,变成了伪码。

参考资料中的第一个连接讲的很好,我就在这里总结一下吧:

  1. CTS是一套语法。相似“英语语法”。它规定了一套约束,例如英语规定全部的字词都是由26个字母组成的(以及其余不少规则)。服从这套语法的语言均可以被当作是英语的某种方言,例如中古英语,现代英语都是英语,而汉语不符合字词由字母组成,因此它不是英语。同理全部服从CTS的语言,均可以被当作.NET框架的语言。
  2. CTS中定义了类型,容许它有属性,字段,方法等。
  3. .NET框架的众多语言各自实现了CTS的一部分功能。作一个不太恰当的类比,C#能够被认为是“美国英语”,F#是“英国英语”而VB是“印度英语”等。他们是英语的各类方言。他们共享一套相同的词汇表,但也各有各的特色。例如颜色在英国英语中的拼写是colour,美国英语则是color。
  4. 因为.NET框架的众多语言在编译时都要转换为IL,所以IL实现的CTS功能是它们的并集,也就是CTS所有的功能。你能够理解为,虽然.NET框架语言那么多,但一编译了以后,就成了一种语言。
  5. .NET框架的众多语言分享CTS的一小部分功能,这部分功能称为CLS(Common Language Specification,公共语言规范)。这是这些语言(的程序集)能够相互使用的前提。若是你建立一个新语言,其实现了CTS的一部分功能,但不包括CLS,那你的语言就不能被其余.NET框架的语言(的程序集)使用。若是你建立的语言甚至不符合CTS,例如你在词汇表中加入了汉字,那很差意思,你建立的语言不能叫英语。

很明显,CLS是CTS的一个子集,并且是最小的子集。(最小功能集)

图片来自CLR via C#。

 

1.5 为何说.NET是平台无关的?

.NET程序集能够在非微软操做系统如Mac OS,各类版本的Linux,以及iOS和Android移动设备上开发和执行。.NET的平台无关性主要体现为:.NET程序集能够在任何的平台上运行,无论是Windows,仍是Mac,只要这个平台拥有将IL转换为机器码,以及加载其余相关程序集的能力(即CLR),而任何机器均可以运行机器码。这相似于Java的虚拟机,只要平台装了Java虚拟机,则这个平台就能够运行Java程序。

 

1.6 CLR(公共语言运行时)

CLR是让程序执行所需的外部服务的集合,相似Java须要JVM虚拟机才能够运行。

它的核心功能(好比即时编译,内存管理,程序集加载,安全性,异常处理和线程同步)可由面向CLR的全部语言使用。例如,CLR容许建立线程,因此面向CLR的全部语言都能建立线程。

CLR是.NET的运行基础,管理.NET程序集的执行。它运行于Windows之上,不少功能仅仅是Windows上的一个wrapper,例如线程,内存管理等,这些其实是Windows在管理。但JIT则是它独有的,若是没有它,就不能把IL变成机器码,计算机也就不认识C#,你也就不能运行C#程序。

在开始运行.NET程序以前,编译器将代码转换为IL。IL代码并不能直接运行,CLR将真正须要用到的程序集导入内存,读取元数据,接着为类型开辟内存空间,执行全部须要的安全检查,并最终运行代码:

  • CLR找到代码中拥有Main方法的类型而且加载这个类型。CLR中一个名为Class loader(类加载程序)的组件负责这项工做。它会从GAC、配置文件、程序集元数据中寻找这个类型,而后将它的类型信息加载到内存中的数据结构中。在Class loader找到并加载完这个类型以后,它的类型信息会被缓存起来,这样就无需再次进行相同的过程。固然,若是这个类型引用了其余的类型,则会致使一连串的程序集加载,这将定义程序代码执行的环境(相似Java的JVM)。注意即便工程很大,有几百个程序集,CLR不会所有加载,只会在真正用到该程序集的时候才加载。
  • 验证。在CLR中,还存在一个验证程序(verifier),该验证程序的工做是在运行时确保代码是类型安全的。它主要校验两个方面,一个是元数据是正确的,一个是IL代码必须是类型安全的,类型的签名必须正确。这是早期绑定验证,验证在运行时以前发生。对于动态类型,此时不作任何检查。
  • 即时编译。(此时就从编译时过渡到了运行时)这一步就是将托管的IL代码编译为能够执行的机器代码的过程,由CLR的即时编译器(JIT Complier)完成。即时编译只有在方法的第一次调用时发生。类型加载程序(Class loader)会为每一个方法插入一个存根。在调用方法时,CLR会检查方法的存根,若是存根为空,则执行JIT编译过程,并将该方法被编译后的本地机器代码地址写入到方法存根中。当第二次对同一方法进行调用时,会再次检查这个存根,若是发现其保存了本地机器代码的地址,则直接跳转到本地机器代码进行执行,无需再次进行JIT编译。JIT编译还会优化本地的代码。

在程序运行时,CLR还负责:

  • 异常处理
  • 内存管理与垃圾回收
  • 线程管理(线程池)

托管代码是必须在CLR下执行的代码,而非托管代码则不须要CLR的支持就能够运行。CLR自己用于管理托管代码,所以它是由非托管代码编写的,并非一个包含了托管代码的程序集,也不能使用IL DASM进行查看。它位于C:\%SystemRoot%\Microsoft.NET\Framework\版本号下,视安装的机器不一样有两个版本,一个是工做站版本的mscorwks.dll,一个是服务器版本的mscorsvr.dll。wks和svr分别表明workstation和server。

CLR via C#这本书选择经过C#做为视角,讨论CLR的各类功能。经过对这本书的阅读,你会对一些实际由CLR进行管理的行为例如垃圾回收,线程管理有更加深入的认识。

 

2. 编译:IL与JIT

面试出现频率:低。不排除部分IL专家会试探性问你一些IL命令,但我相信你答不出来他们也不会在乎。学了IL和没学,通常人看不出来区别,学了IL,也不意味着你就很厉害。我的认为,学IL惟一的用处就在于证实你看到的书上写的各类结论,或者验证一些性能方面的想法。你能够参看这篇文章:http://blog.zhaojie.me/2009/06/my-view-of-il-2-il-shows-little-about-clr.html

重要程度:3/10,常识性了解便可

须要理解的程度:知道IL是中间代码,知道JIT的优势(带缓存的编译),以及它可能会对你的代码进行优化。

 

2.1 什么是IL(CIL)?如何得到IL代码?

在.NET的开发过程当中, IL的官方术语是MSIL或CIL(Common Intermediate Language,即公共中间语言)。所以,IL,MSIL和CIL指的是同一种东西。

当使用支持.NET的编译器编译以后,生成.dll或.exe文件。这文件称做.NET程序集,包含IL和元数据。不一样语言(例如C#和VB)通过不一样编译器(例如C#编译器和VB编译器),编译一段功能类似的代码(区别仅仅在于语法),其IL也基本类似。虽然IL相对C#较为底层,但它仍然是一个十分高级的语言。它并非汇编语言。

能够经过ildasm(在cmd中运行)工具加载任意的.NET程序集并分析它的内容,包括它所包含的IL代码和元数据。注意,高级语言只公开了CLR的全部功能的一个子集,而IL容许开发人员访问CLR全部的功能。

关于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)是动态编译的一种形式,是一种提升程序运行效率的方法。一般,程序有两种运行方式:静态编译与动态编译。静态编译的程序在执行前所有被翻译为机器码,而动态编译执行的则是一句句,边运行边翻译。

即时编译则混合了这两者,一句句编译源代码,可是会将翻译过的代码缓存起来以下降性能损耗。相对于静态编译代码,即时编译的代码能够处理延迟绑定并加强安全性。

CLR的JIT负责将IL编译成机器码。 当程序编译成程序集以后,CLR加载任何须要用到的其余程序集,并开始使用JIT将CIL编译为机器码。JIT编译器会在方法的首次调用时,从类型的元数据中查找方法,并进行检查,例如检查类型是否安全。若是出现了问题,则触发运行时错误。之后对方法的全部调用都以本地代码的形式全速运行,无须从新检查。

 

2.3 本地代码的优化

CLR的JIT编译器会对本地代码进行优化。例如字符串驻留中对常量字符串相加的优化。和没有优化相比,优化以后的代码将得到更出色的性能。但过分的优化可能会出现问题,在CLR via C#的易失构造中,做者举了一个例子。

 1 class Program
 2     {
 3         private static bool s_stopWorker = false;
 4 
 5         static void Main()
 6         {
 7             Console.WriteLine("Main: letting worker run for 2 seconds");
 8             Thread t = new Thread(Worker);
 9             t.Start();
10 
11             Thread.Sleep(2000);
12             s_stopWorker = true;
13             Console.WriteLine("Main: waiting for worker to stop");
14             t.Join();
15         }
16 
17         private static void Worker(object o)
18         {
19             int x = 0;
20             while (!s_stopWorker)
21             {
22                 x++;                
23             }
24             Console.WriteLine("Worker: stopped when x = {0}", x);
25         }
26     }
View Code

若是使用f5呼叫出Visual Studio的调试模式,则程序会像预期的那样正常运行直到结束。使用调试器会形成JIT编译器在Debug模式进行编译,它生成未优化的代码,目的是方便你进行单步调试。若是是选择了x86Release模式进行编译:

它将会生成被CLR优化的代码。值得一提的是,x86编译器是一个更成熟的编译器,执行优化比x64更大胆。x64不会执行上面所说的特定的优化。在再次用f6进行编译以后,用ctrl+f5运行程序,程序将会陷入无限循环

注意:必须用x86+Release编译,而后以非调试模式运行(即Ctrl+F5),才能看到这个效果。问题发生的缘由是,x86的编译优化过分。它发现变量s_stopWorker要么为true要么为false。它还发现这个值在worker方法自己中历来没有变化。所以,编译器会生成代码检查s_stopWorker,若是s_stopWorker为true,就显示“Worker: stopped when x = 0”。若是s_stopWorker为false编译器就生成代码进入一个无限循环,并一直递增x。解决的办法是为s_stopWorker加入修饰词volatile。

PDB文件包含了能够令调试器在本地工做的信息。能够这么说:有了PDB文件,本地的debug才成为可能。若是你打算发布Release版本,则不须要该文件。使用Release模式编译的结果中也不包含PDB文件。例如,你写了一个小的控制台程序给别人用,那么你不须要把\bin\debug里面全部的文件都拷贝给别人,你只须要程序自己,必要的dll和config文件便可。

相关文章
相关标签/搜索