快捷搜索:
来自 计算机编程 2019-06-16 13:24 的文章
当前位置: 67677新澳门手机版 > 计算机编程 > 正文

67677新澳门手机版NET框架基础知识,通俗易懂

csproj工程文件

这里面,csproj是我们最常见的核心文件,CSharp Project,它是用于构建这个项目的工程文件。

csproj是基于xml格式的MSBuild项目文件,其仍然是文本文件,可以打开并修改定义了的工程构造的属性,比如选择性的添加或删除或修改包含在项目中的文件或引用、修改项目版本、将其转换为其它类型项目等。

MSBuild是微软定义的一个用于生成应用程序的平台(Microsoft Build Engine),在这里为VS提供了项目的构造系统,在微软官方文档上有着详细的说明:、

 

  • 什么是.NET Framework
    • 如何在VS中调试.NET Framework源代码
  • 什么是.NET Core
  • 什么是.NET Standard
  • .NET官方开源项目链接

.NET框架基础知识(1)

参考资料:

  • (非常经典的一篇文章)
  • 精通C# (第六版)
  • CLR via C# (第三版)

什么是CLR宿主进程,运行时主机?

那么相对应的,容纳.NET虚拟机的进程就是CLR宿主进程了,该程序称之为运行时主机。

这些运行库的代码,全是由C/C 编写,具体表现为以mscoree.dll为代表的核心dll文件,该dll提供了N多函数用来构建一个CLR环境 ,最后当运行时环境构建完毕(一些函数执行完毕)后,调用_CorDllMain或_CorExeMain来查找并执行托管程序的入口方法(如控制台就是Main方法)。

如果你足够熟悉CLR,那么你完全可以在一个非托管程序中通过调用运行库函数来定制CLR并执行托管代码。
像SqlServer就集成了CLR,可以使用任何 .NET Framework 语言编写存储过程、触发器、用户定义类型、用户定义函数(标量函数和表值函数)以及用户定义的聚合函数。

有关CLR大纲介绍:
CLR集成:
构造CLR的接口:
适用于 .NET Framework 2.0 的宿主接口:
选择CLR版本:

所以C#编写的程序如果想运行就必须要依靠.NET提供的CLR环境来支持。 而CLR是.NET技术框架中的一部分,故只要在Windows系统中安装.NET Framework即可。

 

共享程序集GAC

我上面说了这么多有关CLR加载程序集的细节和规则,事实上,类似于mscorlib.dll、System.dll这样的FCL类库被引用的如此频繁,它已经是我们.NET编程中必不可少的一部分,几尽每个项目都会引用,为了不再每次使用的时候都复制一份,所以计算机上有一个位置专门存储这些我们都会用到的程序集,叫做全局程序集缓存(Global Assembly Cache,GAC),这个位置一般位于C:WindowsMicrosoft.NETassembly和3.5之前版本的C:Windowsassembly。
既然是共享存放的位置,那不可避免的会遇到文件名重复的情况,那么为了杜绝该类情况,规定在GAC中只能存在强名称程序集,每当CLR要加载强名称程序集时,会先通过标识去GAC中查找,而考虑到程序集文件名称一致但版本文化等复杂的情况,所以GAC有自己的一套目录结构。我们如果想将自己的程序集放入GAC中,那么就必须先签名,然后通过如gacutil.exe工具(其存在于命令行工具中 中)来注册至GAC中,值得一提的是在将强名称程序集安装在GAC中,会效验签名。

GAC工具:

1 术语

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

重要程度:3/10

需要理解的程度:知道这些缩写(CLR,BCL,FCL,CTS,CLS)各代表什么即可。仔细读一遍

.NET官方开源项目链接

现在我将给出.NET相关的开源项目地址:
参与.NET和.NET开源项目的起点:

  • .NET Core:
  • .NET Core文档:
  • ASP.NET Core:
  • ASP.NET Core文档:
  • EntityFramework Core框架:
  • ASP.NET Core MVC框架:
  • EntityFramework6:
  • .NET Framework源码:
  • .NET Core基类库:
  • .NET Core CLR:
  • Roslyn编译器:
  • MVC5、Web API2、Web Pages3框架源码:
  • .NET Standard:
  • KestrelHttpServer用于ASP.NET Core的跨平台Web服务器:
  • Visual Studio Code源码:
  • 一些优秀的.NET库、工具、框架、软件开源集合:
  • 一些常用框架对ASP.NET Core和.NET Core的支持报告:
  • 一些.NET下用于支持开发的开源项目集合:
  • 微软出品的分布式框架orleans:
  • ML.NET 用于.NET的开源和跨平台机器学习框架:

2. 编译:IL与JIT

面试出现频率:低。不排除部分IL专家会试探性问你一些IL命令,但我相信你答不出来他们也不会在意。学了IL和没学,一般人看不出来区别,学了IL,也不意味着你就很厉害。个人认为,学IL唯一的用处就在于证明你看到的书上写的各种结论,或者验证一些性能方面的想法。你可以参看这篇文章:

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

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

什么是程序集

2.3 本地代码的优化

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

67677新澳门手机版 167677新澳门手机版 2

 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模式进行编译,它生成未优化的代码,目的是方便你进行单步调试。如果是选择了x86的Release模式进行编译:

67677新澳门手机版 3

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

67677新澳门手机版 4

注意:必须用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文件。例如,你写了一个小的控制台程序给别人用,那么你不需要把bindebug里面所有的文件都拷贝给别人,你只需要程序本身,必要的dll和config文件即可。

Visual Studio

在文章最后,我还要简单的说下Visual Studio。

通过上文得知,只需要一个txt记事本 csc.exe我们就可以开发出一个.NET程序,那么与之相比,.NET提供的开发工具VS有什么不同呢?

我们用记事本 csc.exe来编写一个.NET程序只适合小打小闹,对于真正要开发一个项目而言,我们需要文件管理、版本管理、一个好的开发环境等。而vs ide则就是这样一个集成代码编辑、编译、调试、追踪、测试、部署、协作、插件扩展这样多个组件的集成开发环境,csc.exe的编译功能只是vs ide中的其中之一。使用vside开发可以节省大量的开发时间和成本。

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.NETFramework版本号下,视安装的机器不同有两个版本,一个是工作站版本的mscorwks.dll,一个是服务器版本的mscorsvr.dll。wks和svr分别代表workstation和server。

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

什么是跨语言互操作,什么是CLS

 

强名称程序集

就比如大家都叫张三,姓名都一样,喊一声张三不知道到底在叫谁。这时候我们就必须扩展一下这个名字以让它具有唯一性。

我们可以通过sn.exe或VS对项目右键属性在签名选项卡中采取RSA算法对程序集进行数字签名(加密:公钥加密,私钥解密。签名:私钥签名,公钥验证签名),会将构成程序集的所有文件通过哈希算法生成哈希值,然后通过非对称加密算法用私钥签名,最后公布公钥生成一串token,最终将生成一个由程序集名称、版本号、语言文化、公钥组成的唯一标识,它相当于一个强化的名称,即强名称程序集。
mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

我们日常在VS中的项目默认都没有被签名,所以就是弱名称程序集。强名称程序集是具有唯一标识性的程序集,并且可以通过对比哈希值来比较程序集是否被篡改,不过仍然有很多手段和软件可以去掉程序集的签名。

需要值得注意的一点是:当你试图在已生成好的强名称程序集中引用弱名称程序集,那么你必须对弱名称程序集进行签名并在强名称程序集中重新注册。
之所以这样是因为一个程序集是否被篡改还要考虑到该程序集所引用的那些程序集,根据CLR搜索程序集的规则(下文会介绍),没有被签名的程序集可以被随意替换,所以考虑到安全性,强名称程序集必须引用强名称程序集,否则就会报错:需要强名称程序集。

.NET Framework 4.5中对强签名的更改:

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再将其翻译为机器码。

 

67677新澳门手机版 5                       

 

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

.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。

67677新澳门手机版 6

.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的一个子集。

 

.NET和C#是什么关系

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的一个子集,而且是最小的子集。(最小功能集)

67677新澳门手机版 7

图片来自CLR via C#。

项目模板

VS使用项目模板来基于用户的选择而创建新的项目,也就是新建项目中的那些展示项(如mvc5项目/winform项目等等),具体表现为包含.vstemplate及一些定义的关联文件这样的母版文件。将这些文件压缩为一个 .zip 文件并放在正确的文件夹中时,就会在展示项中予以显示。

用户可以创建或自定义项目模板,也可以选择现有的模板,比如我创建一个控制台项目就会生成一个在.vstemplate中定义好的Program.cs、AssemblyInfo.cs(程序集级别的特性)、App.config、ico、csproj文件

67677新澳门手机版 8

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系列:

sln解决方案

当你用VS来新建一个项目时,VS会先为你新建一个整体的解决方案。这个解决方案表现为.sln和.suo后缀格式的文件,它们均是文本文件,对解决方案右键属性可以进行相应的修改,也可以直接用记事本打开。

在sln中,定义了解决方案的版本及环境,如包含的项目,方案启动项,生成或部署的一些项目配置等,你可以通过修改或重新定义sln来更改你的整个解决方案。
而suo则包含于解决方案建立关联的选项,相当于快照,储存了用户界面的自定义配置、调试器断点、观察窗口设置等这样的东西,它是隐藏文件,可删除但建议不要删除。

我们可以通过对比各版本之间的sln来修改sln,也可以使用网上的一些转换工具,也可以直接点击VS的文件-新建-从现有代码创建项目来让项目在不同VS版本间切换。
Visual Studio 2010 - # Visual Studio 4.0
Visual Studio 2012 - # Visual Studio 4.0
Visual Studio 2013 - # Visual Studio 12.00
Visual Studio 2015 - # Visual Studio 14
Visual Studio 2017 - # Visual Studio 15

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

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

什么是CTS?

如果理解了什么是CLS的话,那么你将很轻松理解什么是CTS。
假设你已经围绕着封装 继承 多态 这3个特性设计出了多款面向对象的语言,你发现大家都是面向对象,都能很好的将现实中的对象模型表达出来。除了语法和功能擅长不同,语言的定义和设计结构其实都差不多一回事。

比如,现实中你看到了一辆小汽车,这辆车里坐着两个人,那么如何用这门语言来表达这样的一个概念和场面?
首先要为这门语言横向定义一个“类型”的概念。接下来在程序中就可以这样表示:有一个汽车类型,有一个人类型,在一个汽车类型的对象内包含着两个人类型的对象,因为要表达出这个模型,你又引入了“对象”的概念 。而现在,你又看到,汽车里面的人做出了开车的这样一个动作,由此你又引入了“动作指令”这样一个概念。
接着,你又恍然大悟总结出一个定理,无论是什么样的“类型”,都只会存在这样一个特征,即活着的 带生命特征的(如人) 和 死的 没有生命特征的(如汽车) 这两者中的一个。最后,随着思想模型的成熟,你发现,这个“类型”就相当于一个富有主体特征的一组指令的集合。
好,然后你开始照葫芦画瓢。你参考其它程序语言,你发现大家都是用class来表示类的含义,用struct表示结构的含义,用new来表示 新建一个对象的含义,于是,你对这部分功能的语法也使用class和new关键字来表示。然后你又发现,他们还用很多关键字来更丰富的表示这些现实模型,比如override、virtual等。于是,在不断的思想升级和借鉴后,你对这个设计语言过程中思想的变化仔细分析,对这套语言体系给抽象归纳,最终总结出一套体系。

于是你对其它人这样说,我总结出了一门语言很多必要的东西如两种主要类别:值类别和引用类别,五个主要类型:类、接口、委托、结构、枚举,我还规定了,一个类型可以包含字段、属性、方法、事件等成员,我还指定了每种类型的可见性规则和类型成员的访问规则,等等等等,只要按照我这个体系来设计语言,设计出来的语言它能够拥有很多不错的特性,比如跨语言,跨平台等,C#和VB.net之所以能够这样就是因为这两门语言的设计符合我这个体系。

 

代码访问安全性

在.NET Framework中还有一个安全策略,叫做 代码访问安全Code Access Security,也就是CAS了。

代码访问安全性在.NET Framework中是用来帮助限制代码对受保护资源和操作的访问权限。
举个例子,我通过创建一个FileIOPermission对象来限制对后续代码对D盘的文件和目录的访问,如果后续代码对D盘进行资源操作则报错。 

67677新澳门手机版 9

FileIOPermission是代码控制访问文件和文件夹的能力。除了FileIOPermission外,还有如PrintingPermission代码控制访问打印机的权限、RegistryPermission代码控制操作注册表的权限、SocketPermission控制接受连接或启动Socket连接的权限。 

对于这些通过代码来对受保护资源和操作的权限限制,也就是这些类名后缀为Permission的类,它们叫做 Permissions(权限),都继承自CodeAccessPermission,都有如Demand,Assert,Deny,PermitOnly,IsSubsetOf,Intersect和Union这些方法,在MSDN上有完整的权限列表:

为了确定代码是否有权访问某一资源或执行某一操作,CLR的安全系统将审核调用堆栈,以将每个调用方获得的权限与要求的权限进行比较。 如果调用堆栈中的任何调用方不具备要求的权限,则会引发安全性异常并拒绝访问。

67677新澳门手机版 10

图出自

而除了Permissions权限,代码访问安全性机制还有 权限集、证据、代码组、策略等概念。这些概念让CAS如此强大,但相应的,它们也让CAS变得复杂,必须为每个特定机器定义正确的PermissionSet和Code Groups才能设置成一个成功的CAS策略。

考虑到这层原因,Microsoft .NET安全小组决定从头开始重建代码访问安全性。在.NET Framework4.0之后,就不再使用之前的那套CAS模型了,而是使用.NET Framework 2.0中引入的安全透明模型,然后稍加修改,修改后的安全透明模型成为保护资源的标准方法,被称之为:安全透明度级别2

安全透明度2介绍:
.NET Framework4.0的安全更改:
一个完整的CAS演示:

对于安全透明度级别2我将不再介绍,感兴趣的可以看我推荐的这2篇文章,对Level2的安全透明度介绍的比较详细,包括实践、迁移。


须注意:
.NET平台上的安全机制,仅仅是.NET平台上的,因此它只限制于托管代码,我们可以直接调用非托管代码或进程通信间接调用非托管代码等多个手段来突破对托管代码 操作资源的限制。

事实上,我们在平常项目中代码编写的安全机制(业务逻辑身份验证、项目框架验证)与这些平台级的安全机制没什么不同。我们可以理解为代码写的位置不一样,.NET安全机制是写在CLR组件中,而我们的安全机制是写在上层的代码中。这些平台级的标识更多的是和操作系统用户有关,而我们项目代码中的标识则是和在数据库中注册的用户有关, 大家都是通过if else来去判断,判断的主体和格局不一样,逻辑本质都是相同的。

NET Core不支持代码访问安全性和安全性透明性。

 

.NET程序执行图

1.3 框架类库(FCL)

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

 

Visual Studio

 

什么是基础类库BCL?

当你通过VS创建一个项目后,你这个项目就已经引用好了通过.NET下的语言编写好的一些类库。比如控制台中你直接就可以用ConSole类来输出信息,或者using System.IO 即可通过File类对文件进行读取或写入操作,这些类都是微软帮你写好的,不用你自己去编写,它帮你编写了一个面向.NET的开发语言中使用的基本的功能,这部分类,我们称之为BCL(Base Class Library), 基础类库,它们大多都包含在System命名空间下。

基础类库BCL包含:基本数据类型,文件操作,集合,自定义属性,格式设置,安全属性,I/O流,字符串操作,事件日志等的类型

 

项目的依赖顺序

如果没有通过config或者在代码中来设定CLR搜索程序集的规则,那么CLR就按照默认的也就是我上述所说的模式来寻找。
所以如果我们通过csc.exe来编译项目,引用了其它程序集的话,通常需要将那些程序集复制到同一目录下。故而每当我们通过VS编译器对项目右键重新生成项目(重新编译)时,VS都会将引用的程序集给复制一份到项目bin输出目录Debug文件夹下,我们可以通过VS中对引用的程序集右键属性-复制本地 True/Flase 来决定这一默认行为。

值得一提的是,项目间的生成是有序生成的,它取决于项目间的依赖顺序。
比如Web项目引用BLL项目,BLL项目引用了DAL项目。那么当我生成Web项目的时候,因为我要注册Bll程序集,所以我要先生成Bll程序集,而BLL程序集又引用了Dal,所以又要先生成Dal程序集,所以程序集生成顺序就是Dal=>BLL=>Web,项目越多编译的时间就越久。

程序集之间的依赖顺序决定了编译顺序,所以在设计项目间的分层划分时不仅要体现出层级职责,还要考虑到依赖顺序。代码存放在哪个项目要有讲究,不允许出现互相引用的情况,比如A项目中的代码引用B,B项目中的代码又引用A。

  • 张子阳

AOT编译

CLR的内嵌编译器是即时性的,这样的一个很明显的好处就是可以根据当时本机情况生成更有利于本机的优化代码,但同样的,每次在对代码编译时都需要一个预热的操作,它需要一个运行时环境来支持,这之间还是有消耗的。

而与即时编译所对应的,就是提前编译了,英文为Ahead of Time Compilation,简称AOT,也称之为静态编译。
在.NET中,使用Ngen.exe或者开源的.NET Native可以提前将代码编译成本机指令。

Ngen是将IL代码提前给全部编译成本机代码并安装在本机的本机映像缓存中,故而可以减少程序因JIT预热的时间,但同样的也会有很多注意事项,比如因JIT的丧失而带来的一些特性就没有了,如类型验证。Ngen仅是尽可能代码提前编译,程序的运行仍需要完整的CLR来支持。

.NET Native在将IL转换为本机代码的时候,会尝试消除所有元数据将依靠反射和元数据的代码替换为静态本机代码,并且将完整的CLR替换为主要包含垃圾回收器的重构运行时mrt100_app.dll。

.NET Native:
Ngen.exe:
Ngen与.NET Native比较:


现在,我们可以通过ILDASM工具(一款查看程序集IL代码的软件,在Microsoft SDKs目录中的子目录中)来查看该程序集的元数据表和Main方法中间码。

67677新澳门手机版 11

c#源码第一行代码:string rootDirectory = Environment.CurrentDirectory;被翻译成IL代码: call string [mscorlib/*23000001*/]System.Environment/*01000004*/::get_CurrentDirectory() /* 0A000003 */ 

这句话意思是调用 System.Environment类的get_CurrentDirectory()方法(属性会被编译为一个私有字段 对应get/set方法)。

点击视图=>元信息=>显示,即可查看该程序集的元数据。
我们可以看到System.Environment标记值为01000004,在TypeRef类型引用表中找到该项:
67677新澳门手机版 12

注意图,TypeRefName下面有该类型中被引用的成员,其标记值为0A000003,也就是get_CurrentDirectory了。
而从其ResolutionScope指向位于0x23000001而得之,该类型存在于mscorlib程序集。
67677新澳门手机版 13

于是我们打开mscorlib.dll的元数据清单,可以在类型定义表(TypeDef)找到System.Environment,可以从元数据得知该类型的一些标志(Flags,常见的public、sealed、class、abstract),也得知继承(Extends)于System.Object。在该类型定义下还有类型的相关信息,我们可以在其中找到get_CurrentDirectory方法。 我们可以得到该方法的相关信息,这其中表明了该方法位于0x0002b784这个相对虚地址(RVA),接着JIT在新地址处理CIL,周而复始。

元数据在运行时的作用:

1.2 基础类库(BCL)

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

当安装.NET Framework时,所有的基础类库被部署到全局程序集缓存(GAC)。它的位置一般在C:Windowsassembly。所以你不需要在你的工程中手动引用任何的基础类库,它们会被自动引用。如果你从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 AssembliesMicrosoftFramework.NETFrameworkv4.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。源码在

什么是基元类型?

2.2 什么是JIT?还有什么其他编译方式?何时使用到JIT?

即时编译(英语:Just-in-time compilation)是动态编译的一种形式,是一种提高程序运行效率的方法。通常,程序有两种运行方式:静态编译与动态编译。静态编译的程序在执行前全部被翻译为机器码,而动态编译执行的则是一句句,边运行边翻译。

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

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

堆栈和堆的区别

堆和堆栈就内存而言只不过是地址范围的区别。不过堆栈的数据结构和其存储定义让其在时间和空间上都紧密的存储,这样能带来更高的内存密度,能在CPU缓存和分页系统表现的更好。故而访问堆栈的速度总体来说比访问堆要快点。

很明显,CLS是CTS的一个子集,而且是最小的子集。

目录

GC堆回收

那么除了通过new对象而达到代的阈(临界)值时,还有什么能够导致垃圾堆进行垃圾回收呢? 还可能windows报告内存不足、CLR卸载AppDomain、CLR关闭等其它特殊情况。

或者,我们还可以自己通过代码调用。

.NET有GC来帮助开发人员管理内存,并且版本也在不断迭代。GC帮我们托管内存,但仍然提供了System.GC类让开发人员能够轻微的协助管理。 这其中有一个可以清理内存的方法(并没有提供清理某个对象的方法):GC.Collect方法,可以对所有或指定代进行即时垃圾回收(如果想调试,需在release模式下才有效果)。这个方法尽量别用,因为它会扰乱代与代间的秩序,从而让低代的垃圾对象跑到生命周期长的高代中。

GC还提供了,判断当前对象所处代数、判断指定代数经历了多少次垃圾回收、获取已在托管堆中分配的字节数这样的三个方法,我们可以从这3个方法简单的了解托管堆的情况。

托管世界的内存不需要我们打理,我们无法从代码中得知具体的托管对象的大小,你如果想追求对内存最细微的控制,显然C#并不适合你,不过类似于有关内存把控的这部分功能模块,我们可以通过非托管语言来编写,然后通过.NET平台的P/Invoke或COM技术(微软为CLR定义了COM接口并在注册表中注册)来调用。

像FCL中的源码,很多涉及到操作系统的诸如 文件句柄、网络连接等外部extren的底层方法都是非托管语言编写的,对于这些非托管模块所占用的资源,我们可以通过隐式调用析构函数(Finalize)或者显式调用的Dispose方法通过在方法内部写上非托管提供的释放方法来进行释放。

像文中示例的socket就将释放资源的方法写入Dispose中,析构函数和Close方法均调用Dispose方法以此完成释放。事实上,在FCL中的使用了非托管资源的类大多都遵循IDispose模式。而如果你没有释放非托管资源直接退出程序,那么操作系统会帮你释放该程序所占的内存的。

.NET程序执行原理

什么是CLR,.NET虚拟机?

实际上,.NET不仅提供了自动内存管理的支持,他还提供了一些列的如类型安全、应用程序域、异常机制等支持,这些 都被统称为CLR公共语言运行库。

CLR是.NET类型系统的基础,所有的.NET技术都是建立在此之上,熟悉它可以帮助我们更好的理解框架组件的核心、原理。
在我们执行托管代码之前,总会先运行这些运行库代码,通过运行库的代码调用,从而构成了一个用来支持托管程序的运行环境,进而完成诸如不需要开发人员手动管理内存,一套代码即可在各大平台跑的这样的操作。

这套环境及体系之完善,以至于就像一个小型的系统一样,所以通常形象的称CLR为".NET虚拟机"。那么,如果以进程为最低端,进程的上面就是.NET虚拟机(CLR),而虚拟机的上面才是我们的托管代码。换句话说,托管程序实际上是寄宿于.NET虚拟机中。

.NET程序执行原理

好的,现在我们已经有了一个demo.exe的可执行程序,它是如何被我们运行的?。

C#源码被编译成程序集,程序集内主要是由一些元数据表和IL代码构成,我们双击执行该exe,Windows加载器将该exe(PE格式文件)给映射到虚拟内存中,程序集的相关信息都会被加载至内存中,并查看PE文件的入口点(EntryPoint)并跳转至指定的mscoree.dll中的_CorExeMain函数,该函数会执行一系列相关dll来构造CLR环境,当CLR预热后调用该程序集的入口方法Main(),接下来由CLR来执行托管代码(IL代码)。

建议

我热爱编程。

我知道大多数人对技术的积累都是来自于平常工作中,工作中用到的就去学,用不到就不学,学一年的知识,然后用个五六年。
我也能理解人的理想和追求不同,有的人可能就想平淡点生活。有的人可能是过了拼劲,习惯了安逸。有的人已经认命了。
而我现在也每天饱满工作没多少时间,但在下班之余我仍然坚持每天都看一看书。
想学没时间学,想拼不知道往哪拼。有埋汰自己脑袋笨的,有说自己不感兴趣的。有明明踌躇满志,但总三天捕鱼两天晒网的。我身边的朋友大多都这样。

我想说,尽管我们每个人的境遇、思想、规划不同,但我肯定大家大部分是出于生计而工作。
而出于生计,那就是为了自己。而既然是为了自己,那就别每天浑浑噩噩过,即使你因各种原因而没有斗志。

编程来不得虚的,如果你没走上管理,那么你的技术好就是好,不好就是不好,混不得,一分技术一分钱。自己不扎实,你运气就不可能太好。
技术是相通的,操作系统、通信、数据结构、协议标准、技术规范、设计模式,语言只是门工具。要知其然也要知其所以然,只知道1个梨 1个梨=2个梨,不知道1个苹果 1个苹果等于啥就悲剧了。

那怎样提升自己?肯定不能像之前那样被动的去学习了。
光靠工作中的积累带来的提升是没有多少。你不能靠1年的技术重复3年的劳动,自己不想提升就不能怨天尤人。
上班大家都一样,我认为成功与否取决于你的业余时间。你每天下班无论再苦都要花一个小时来学习,学什么都行,肯定能改变你的人生轨迹。
比如你每天下班后都用一小时来学一个概念或技术点,那么300天就是300个概念或者技术点,这是何等的恐怖。

当然,这里的学要有点小方法小技巧的。不能太一条道摸到黑的那种,虽然这样最终也能成功,并且印象还深刻,但是总归效率是有点低的。
比如你从网上下载个项目源码,你项目结构不知道,该项目运用技术栈也不太了解,就一点一点的开始解读。这是个提升的好方法,但这样很累,可以成功,但是很慢。见的多懂的少,往往会因为一个概念上的缺失而在一个细小的问题上浪费很长时间。或者说一直漫无目的的看博客来了解技术,那样获取的知识也不系统。

我的建议是读书,书分两类,一类是 讲底层概念的 一类是 讲上层技术实现的。
可以先从上层技术实现的书读起(如何连接数据库、如何写网页、如何写窗体这些)。在有一定编程经验后就从底层概念的书开始读,操作系统的、通信的、数据库的、.NET相关组成的这些...
读完之后再回过头读这些上层技术的书就会看的更明白更透彻,最后再琢磨git下来的项目就显得轻松了。

就.NET CLR组成这一块中文书籍比较少,由浅到深推荐的书有 你必须知道的.NET(挺通俗),CLR C#(挺通俗,进阶必看),如果你想进一步了解CLR,可以看看园子里 包建强 和中道学友 .NET 2.0 IL Assembler的机器翻译版,同时我也建议从调试的方面入手,如 NET高级调试(好多.NET文件调试、反编译的文章都是参考这本书和Apress.Expert.dot.NET.2.0.IL.Assembler(这本书我有机器翻译版)的内容)或者看看Java的JVM的文章。
欢迎加群和我交流(书籍我都放在群文件里了)

 

现在技术发展很快,我建议大家有基础的可以直接看官方文档,(详细链接我已经在各小节给出)以下是部分常用总链接:

asp.net指南:
Visual Studio IDE 指南:
C# 指南:
.NET指南:
微软开发文档:

最后送给大家我经常做的两句话:
1.先问是不是,再问怎样做,最后我一定会问 为什么
2.没人比谁差多少,相信自己,坚持不断努力,你也能成功

 

我喜欢和我一样的人交朋友,不被环境影响,自己是自己的老师,欢迎加群 .Net web交流群, QQ群:166843154 欲望与挣扎

作者:小曾
出处: 欢迎转载,但请保留以上完整文章,在显要地方显示署名以及原文链接。
.Net交流群, QQ群:166843154 欲望与挣扎

 

我的博客即将同步至腾讯云 社区,邀请大家一同入驻:

JIT编译

前面说了,计算机最终只识别二进制的机器码,在CLR下有一个用来将IL代码转换成机器码的引擎,称为Just In Time Compiler,简称JIT,CLR总是先将IL代码按需通过该引擎编译成机器指令再让CPU执行,在这期间CLR会验证代码和元数据是否类型安全(在对象上只调用正确定义的操作、标识与声称的要求一致、对类型的引用严格符合所引用的类型),被编译过的代码无需JIT再次编译,而被编译好的机器指令是被存在内存当中,当程序关闭后再打开仍要重新JIT编译。

计算机是如何运行程序的?

程序集的加载方式

对于自身程序集内定义的类型,我们可以直接从自身程序集中的元数据中获取,对于在其它程序集中定义的类型,CLR会通过一组规则来在磁盘中找到该程序集并加载在内存。

CLR在查找引用的程序集的位置时候,第一个判断条件是 判断该程序集是否被签名。
什么是签名?

托管堆模型

而引用类型相比值类型就有点特殊,newobj创建一个引用类型,因其类型内的引用对象可以指向任何类型,故而无法准确得知其固定大小,所以像对于引用类型这种无法预知的容易产生内存碎片的动态内存,我们把它放到托管堆中存储。

托管堆由GC托管,其分配的核心在于堆中维护着一个nextObjPtr指针,我们每次实例(new)一个对象的时候,CLR将对象存入堆中,并在栈中存放该对象的起始地址,然后该指针都会根据该对象的大小来计算下一个对象的起始地址。不同于值类型直接在栈中存放值,引用类型则还需要在栈中存放一个代表(指向)堆中对象的值(地址)。

而托管堆又可以因存储规则的不同将其分类,托管堆可以被分为3类:

  • 1.用于托管对象实例化的垃圾回收堆,又以存储对象大小分为小对象(<85000byte)的GC堆(SOH,Small Object Heap)和用于存储大对象实例的(>=85000byte)大对象堆(LOG,Larage Object Heap)。
  • 2.用于存储CLR组件和类型系统的加载(Loader)堆,其中又以使用频率分为经常访问的高频堆(里面包含有MethodTables方法表, MeghodDescs方法描述, FieldDescs方法描述和InterfaceMaps接口图),和较低的低频堆,和Stub堆(辅助代码,如JIT编译后修改机器代码指令地址环节)。
  • 3.用于存储JIT代码的堆及其它杂项的堆。

加载程序集就是将程序集中的信息给映射在加载堆,对产生的实例对象存放至垃圾回收堆。前文说过应用程序域是指通过CLR管理而建立起的逻辑上的内存边界,那么每个域都有其自己的加载堆,只有卸载应用程序域的时候,才会回收该域对应的加载堆。

而加载堆中的高频堆包含的有一个非常重要的数据结构表---方法表,每个类型都仅有一份方法表(MethodTables),它是对象的第一个实例创建前的类加载活动的结果,它主要包含了我们所关注的3部分信息:

  • 1包含指向EEClass的一个指针。EEClass是一个非常重要的数据结构,当类加载器加载到该类型时会从元数据中创建出EEClass,EEClass里主要存放着与类型相关的表达信息。
  • 2包含指向各自方法的方法描述器(MethodDesc)的指针逻辑组成的线性表信息:继承的虚函数, 新虚函数, 实例方法, 静态方法。
  • 3包含指向静态字段的指针。

那么,实例一个对象,CLR是如何将该对象所对应的类型行为及信息的内存位置(加载堆)关联起来的呢?

原来,在托管堆上的每个对象都有2个额外的供于CLR使用的成员,我们是访问不到的,其中一个就是类型对象指针,它指向位于加载堆中的方法表从而让类型的状态和行为关联了起来, 类型指针的这部分概念我们可以想象成obj.GetType()方法获得的运行时对象类型的实例。而另一个成员就是同步块索引,其主要用于2点:1.关联内置SyncBlock数组的项从而完成互斥锁等目的。 2.是对象Hash值计算的输入参数之一。

67677新澳门手机版 14

上述gif是我简单画的一个图,可以看到对于方法中申明的值类型变量,其在栈中作为一块值表示,我们可以直接通过c#运算符sizeof来获得值类型所占byte大小。而方法中申明的引用类型变量,其在托管堆中存放着对象实例(对象实例至少会包含上述两个固定成员以及实例数据,可能),在栈中存放着指向该实例的地址。

当我new一个引用对象的时候,会先分配同步块索引(也叫对象头字节),然后是类型指针,最后是类型实例数据(静态字段的指针存在于方法表中)。会先分配对象的字段成员,然后分配对象父类的字段成员,接着再执行父类的构造函数,最后才是本对象的构造函数。这个多态的过程,对于CLR来说就是一系列指令的集合,所以不能纠结new一个子类对象是否会也会new一个父类对象这样的问题。而也正是因为引用类型的这样一个特征,我们虽然可以估计一个实例大概占用多少内存,但对于具体占用的大小,我们需要专门的工具来测量。

对于引用类型,u2=u1,我们在赋值的时候,实际上赋的是地址,那么我改动数据实际上是改动该地址指向的数据,这样一来,因为u2和u1都指向同一块区域,所以我对u1的改动会影响到u2,对u2的改动会影响到u1。如果我想互不影响,那么我可以继承IClone接口来实现内存克隆,已有的CLR实现是浅克隆方法,但也只能克隆值类型和String(string是个特殊的引用类型,对于string的更改,其会产生一个新实例对象),如果对包含其它引用类型的这部分,我们可以自己通过其它手段实现深克隆,如序列化、反射等方式来完成。而如果引用类型中包含有值类型字段,那么该字段仍然分配在堆上。

对于值类型,a=b,我们在赋值的时候,实际上是新建了个值,那么我改动a的值那就只会改动a的值,改动b的值就只会改动b的值。而如果值类型(如struct)中包含的有引用类型,那么仍是同样的规则,引用类型的那部分实例在托管堆中,地址在栈上。

我如果将值类型放到引用类型中(如:object a=3),会在栈中生成一个地址,在堆中生成该值类型的值对象,还会再生成这类型指针和同步块索引两个字段,这也就是常说装箱,反过来就是拆箱。每一次的这样的操作,都会涉及到内存的分布、拷贝,可见,装箱和拆箱是有性能损耗,因此应该减少值类型和引用类型之间转换的次数。
但对于引用类型间的子类父类的转换,仅是指令的执行消耗,几尽没有开销。

跨语言和跨平台是什么

非托管的异常

我们编写的程序有的模块是由托管代码编写,有的模块则调用了非托管代码。在.NET Framework中也有一套基于此操作系统SEH的异常机制,理想的机制设定下我们可以直接通过catch(e)或catch来捕获指定的异常和框架设计人员允许我们捕获的异常。

而异常类型的级别也有大有小,有小到可以直接框架本身或用代码处理的,有大到需要操作系统的异常机制来处理。.NET会对那些能让程序崩溃的异常类型给进行标记,对于这部分异常,在.NET Framework 4.0之前允许开发人员在代码中自己去处理,但4.0版本之后有所变更,这些被标记的异常默认不会在托管环境中抛出(即无法catch到),而是由操作系统的SEH机制去处理。
不过如果你仍然想在代码中捕获处理这样的异常也是可以的,你可以对需要捕获的方法上标记[System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptionsAttribute]特性,就可以在该方法内通过catch捕获到该类型的异常。你也可以通过在配置文件中添加运行时节点来对全局进行这样的一个配置:

<runtime>
     <legacyCorruptedStateExceptionsPolicy enabled="true" />
</runtime>

HandleProcessCorruptedStateExceptions特性:
SEHException类:
处理损坏状态异常博客专栏:

.NET的安全性

项目属性杂项

现在,简单说明一下csproj文件的一些核心元素。我们用vs新建一个控制台项目,然后对项目右键属性打开项目属性,在应用程序页我们可以定义:程序集名称(生成出来的程序集以程序集名称作为文件名,相当于csc中的/out)、默认命名空间(每次新建类里面显示的命名空间)、目标框架、应用程序类型、程序集信息(AssemblyInfo中的信息)、启动对象(可同时存在多个Main方法,需指定其中一个为入口对象)、程序集资源(一些可选的图标及文件)

1.在生成页有:

  • 条件编译符号(全局的预编译#define指令,不用在每个文件头部定义,相当于csc中的/define)
  • 定义DEBUG/TRACE常量(用于调试输出的定义变量,如智能追踪的时候可以输出该变量)
  • 目标平台(指定当前面向什么处理器生成的程序集,相当于csc中的/platform。选择x86则生成的程序集生成32位程序,能在32/64位Intel处理器中使用。选择x64则生成64位,只能在64位系统中运行。选择Any CPU则32位系统生成32位,64位系统则生成64位。注意:编译平台和目标调用平台必须保持一致,否则报错。生成的32位程序集不能调用64位程序集,64位也不能调用32位)、首选32位(如果目标平台是Any CPU并且项目是应用程序类型,则生成的是32位程序集)
  • 允许不安全代码(unsafe开关,在c#中进行指针编程,如调换a方法和b方法的地址)
  • 优化代码(相当于csc中的/optimize,优化IL代码让调试难以进行,优化JIT代码)
  • 输出路径(程序集输出目录,可选择填写相对路径目录或绝对路径目录)
  • XML文档文件(相当于csc中的/doc,为程序集生成文档注释文件,浏览对方程序集对象就可以看到相关注释,VS的智能提示技术就运用于此)
  • 为COM互操作注册(指示托管应用程序将公开一个 COM 对象,使COM对象可以与托管应用程序进行交互)

2.在高级生成设置中有:语言版本(可以选择C#版本)、调试信息(相当于csc中的/debug。选择none则不生成任何调试信息,无法调试。选择full则允许将调试器附加到运行程序,生成pdb调试文件。选择pdb-only,自.NET2.0开始与full选项完全相同,生成相同的pdb调试文件。)、文件对齐(指定输出文件中节的大小)、DLL基址(起点地址)

3.在生成事件选项中可以设置生成前和生产后执行的命令行,我们可以执行一些命令。

4.在调试选项中有一栏叫:启用Visual Studio承载进程,通过在vshost.exe中加载运行项目程序集,这个选项可以增加程序的调试性能,启用后会自动在输出目录生成{程序集名称}.vshost.exe这样一个文件,只有当当前项目不是启动项目的时候才能删除该文件。

Windows系统自带.NET Framework

选class还是struct

那么我到底是该new一个class呢还是选择struct呢?

通过上文知道对于class,用完之后对象仍然存在托管堆,占用内存。对于struct,用完之后直接由操作系统销毁。那么在实际开发中定义类型时,选择class还是struct就需要注意了,要综合应用场景来辨别。struct存在于栈上,栈和托管堆比较,最大的优势就是即用即毁。所以如果我们单纯的传递一个类型,那么选择struct比较合适。但须注意线程堆栈有容量限制,不可多存放超大量的值类型对象,并且因为是值类型直接传递副本,所以struct作为方法参数是线程安全的,但同样要避免装箱的操作。而相比较class,如果类型中还需要多一些封装继承多态的行为,那么class当然是更好的选择。

应用程序域

程序集搜索规则

事实上,按照存储位置来说,程序集分为共享(全局)程序集和私有程序集。

CLR查找程序集的时候,会先判断该程序集是否被强签名,如果强签名了那么就会去共享程序集的存储位置(后文的GAC)去找,如果没找到或者该程序集没有被强签名,那么就从该程序集的同一目录下去寻找。

强名称程序集是先找到与程序集名称(VS中对项目右键属性应用程序->程序集名称)相等的文件名称,然后 按照唯一标识再来确认,确认后CLR加载程序集,同时会通过公钥效验该签名来验证程序集是否被篡改(如果想跳过验证可查阅),如果强名称程序集被篡改则报错。

而弱名称程序集则直接按照与程序集名称相等的文件名称来找,如果还是没有找到就以该程序集名称为目录的文件夹下去找。总之,如果最终结果就是没找到那就会报System.IO.FileNotFoundException异常,即尝试访问磁盘上不存在的文件失败时引发的异常。

注意:此处文件名称和程序集名称是两个概念,不要模棱两可,文件CLR头内嵌程序集名称。

举个例子:
我有一个控制台程序,其路径为D:DemoDebugdemo.exe,通过该程序的元数据得知,其引用了一个程序集名称为aa的普通程序集,引用了一个名为bb的强名称程序集,该bb.dll的强名称标识为:xx001。
现在CLR开始搜索程序集aa,首先它会从demo.exe控制台的同一目录(也就是D:DemoDebug)中查找程序集aa,搜索文件名为aa.dll的文件,如果没找到就在该目录下以程序集名称为目录的目录中查找,也就是会查 D:DemoDebugaaaa.dll,这也找不到那就报错。
然后CLR开始搜索程序集bb,CLR从demo.exe的元数据中发现bb是强名称程序集,其标识为:xx001。于是CLR会先从一个被定义为GAC的目录中去通过标识找,没找到的话剩下的寻找步骤就和寻找aa一样完全一致了。

当然,你也可以通过配置文件config中(配置文件存在于应用程序的同一目录中)人为增加程序集搜索规则:
1.在运行时runtime节点中,添加privatePath属性来添加搜索目录,不过只能填写相对路径: 

<runtime>
            <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
                <probing privatePath="relative1;relative2;" /> //程序集当前目录下的相对路径目录,用;号分割
            </assemblyBinding>
</runtime>

2.如果程序集是强签名后的,那么可以通过codeBase来指定网络路径或本地绝对路径。

<runtime>
            <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
                <dependentAssembly>
                    <assemblyIdentity name="myAssembly"
                                      publicKeyToken="32ab4ba45e0a69a1"
                                      culture="neutral" />
                    <codeBase version="2.0.0.0"
                              href="http://www.litwareinc.com/myAssembly.dll" />
                </dependentAssembly>
            </assemblyBinding>
</runtime>

当然,我们还可以在代码中通过AppDomain类中的几个成员来改变搜索规则,如AssemblyResolve事件、AppDomainSetup类等。

有关运行时节点的描述:

那么,什么是CTS呢?

当你需要设计面向.Net的语言时所需要遵循一个体系(.Net平台下的语言都支持的一个体系)这个体系就是CTS(Common Type System 公共类型系统),它包括但不限于:

  • 建立用于跨语言执行的框架。
  • 提供面向对象的模型,支持在 .NET 实现上实现各种语言。
  • 定义处理类型时所有语言都必须遵守的一组规则(CLS)。
  • 提供包含应用程序开发中使用的基本基元数据类型(如 Boolean、Byte、Char 等)的库。

上文的CLS是CTS(Common Type System 公共类型系统)这个体系中的子集。
一个编程语言,如果它能够支持CTS,那么我们就称它为面向.NET平台的语言。
官方CTS介绍:

微软已经将CTS和.NET的一些其它组件,提交给ECMA以成为公开的标准,最后形成的标准称为CLI(Common Language Infrastructure)公共语言基础结构。
所以有的时候你见到的书籍或文章有的只提起CTS,有的只提起CLI,请不要奇怪,你可以宽泛的把他们理解成一个意思,CLI是微软将CTS等内容提交给国际组织计算机制造联合会ECMA的一个工业标准。

程序集的规则

上文我通过ILDASM来描述CLR执行代码的方式,但还不够具体,还需要补充的是对于程序集的搜索方式。

对于System.Environment类型,它存在于mscorlib.dll程序集中,demo.exe是个独立的个体,它通过csc编译的时候只是注册了引用mscorlib.dll中的类型的引用信息,并没有记录mscorlib.dll在磁盘上的位置,那么,CLR怎么知道get_CurrentDirectory的代码?它是从何处读取mscorlib.dll的?
对于这个问题,.NET有个专门的概念定义,我们称为 程序集的加载方式。

.NET Core是什么

有丑才有美,有低才有高,概念是比较中诞生的。.NET Core就是如此,它是其它操作系统的.NET Framework翻版实现。

操作系统不止Windows,还有Mac和类Linux等系统, .NET的实现 如果按操作系统来横向分割的话,可以分为 Windows系统下的 .NET Framework 和 兼容多个操作系统的 .NET Core。

我们知道,一个.NET程序运行核心在于.NET CLR,为了能让.NET程序在其它平台上运行,一些非官方社区和组织为此开发了在其它平台下的.NET实现(最为代表的是mono,其团队后来又被微软给合并了 ),但因为不是官方,所以在一些方面多少有些缺陷(如FCL),后来微软官方推出了.NET Core,其开源在Github中,并被收录在NET基金会(.NET Foundation,由微软公司成立与赞助的独立自由软件组织,其目前收录包括.NET编译器平台("Roslyn")以及ASP.NET项目系列,.NET Core,Xamarin Forms以及其它流行的.NET开源框架),旨在真正的 .NET跨平台。

.NET Core是.NET 技术框架组成在Windows.macOS.Linux系统下的具体的实现。
.NET Core是一个开源的项目,其由 Microsoft 和 GitHub 上的 .NET 社区共同维护,但 这份工作仍然是巨大的,因为在早期对.NET上的定义及最初的实现一直是以Windows系统为参照及载体,一些.NET机制实际上与Windows系统耦合度非常高,有些属于.NET自己体系内的概念,有些则属于Windows系统api的封装。 那么从Windows转到其它平台上,不仅要实现相应的CLR,还要舍弃或重写一部分BCL,因而,.NET Core在概念和在项目中的行为与我们平常有些不同。

比如,NET Core不支持AppDomains、远程处理、代码访问安全性 (CAS) 和安全透明度,任何有关该概念的库代码都应该被替换。
这部分代码它不仅指你项目中的代码,还指你项目中using的那些程序集代码,所以你会在github上看到很多开源项目都在跟进对.NET Core的支持,并且很多开发者也尝试学习.NET Core,这也是一种趋势。

.NET Core指南
.NET基金会:
.NET Core跨平台的行为变更:
微软宣布.NET开发环境将开源 :

用csc.exe进行编译

System.Object的意义

  • sln解决方案
  • 项目模板
  • csproj工程文件
  • 项目属性杂项
  • IntelliTrace智能追溯
  • 链接

性能建议

虽然我们可以选择适合的GC工作模式来改善垃圾回收时的表现,但在实际开发中我们更应该注意减少不必要的内存开销。

几个建议是,减换需要创建大量的临时变量的模式、考虑对象池、大对象使用懒加载、对固定容量的集合指定长度、注意字符串操作、注意高频率的隐式装箱操作、延迟查询、对于不需要面向对象特性的类用static、需要高性能操作的算法改用外部组件实现(p/invoke、com)、减少throw次数、注意匿名函数捕获的外部对象将延长生命周期、可以阅读GC相关运行时配置在高并发场景注意变换GC模式...

对于.NET中改善性能可延伸阅读 、

内存

应用程序域把资源给隔离开,这个资源,主要指内存。那么什么是内存呢?

要知道,程序运行的过程就是电脑不断通过CPU进行计算的过程,这个过程需要读取并产生运算的数据,为此我们需要一个拥有足够容量能够快速与CPU交互的存储容器,这就是内存了。对于内存大小,32位处理器,寻址空间最大为2的32次方byte,也就是4G内存,除去操作系统所占用的公有部分,进程大概能占用2G内存,而如果是64位处理器,则是8T。

而在.NET中,内存区域分为堆栈和托管堆。

GC管理器

值得注意的是,当我new完一个对象不再使用的时候,这个对象在堆中所占用的内存如何处理?
在非托管世界中,可以通过代码手动进行释放,但在.NET中,堆完全由CLR托管,也就是说GC堆是如何具体来释放的呢?

当GC堆需要进行清理的时候,GC收集器就会通过一定的算法来清理堆中的对象,并且版本不同算法也不同。最主要的则为Mark-Compact标记-压缩算法。
这个算法的大概含义就是,通过一个图的数据结构来收集对象的根,这个根就是引用地址,可以理解为指向托管堆的这根关系线。当触发这个算法时,会检查图中的每个根是否可达,如果可达就对其标记,然后在堆上找到剩余没有标记(也就是不可达)的对象进行删除,这样,那些不在使用的堆中对象就删除了。

前面说了,因为nextObjPtr的缘故,在堆中分配的对象都是连续分配的,因为未被标记而被删除,那么经过删除后的堆就会显得支零破碎,那么为了避免空间碎片化,所以需要一个操作来让堆中的对象再变得紧凑、连续,而这样一个操作就叫做:Compact压缩。
而对堆中的分散的对象进行挪动后,还会修改这些被挪动对象的指向地址,从而得以正确的访问,最后重新更新一下nextObjPtr指针,周而复始。

而为了优化内存结构,减少在图中搜索的成本,GC机制又为每个托管堆对象定义了一个属性,将每个对象分成了3个等级,这个属性就叫做:代,0代、1代、2代。

每当new一个对象的时候,该对象都会被定义为第0代,当GC开始回收的时候,先从0代回收,在这一次回收动作之后,0代中没有被回收的对象则会被定义成第1代。当回收第1代的时候,第1代中没有被清理掉的对象就会被定义到第2代。
CLR初始化时会为0/1/2这三代选择一个预算的容量。0代通常以256 KB-4 MB之间的预算开始,1代的典型起始预算为512 KB-4 MB,2代不受限制,最大可扩展至操作系统进程的整个内存空间。

比如第0代为256K,第1代为2MB。我们不停的new对象,直到这些对象达到256k的时候,GC会进行一次垃圾回收,假设这次回收中回收了156k的不可达对象,剩余100k的对象没有被回收,那么这100k的对象就被定义为第1代。现在就变成了第0代里面什么都没有,第1代里放的有100k的对象。这样周而复始,GC清除的永远都只有第0代对象,除非当第一代中的对象累积达到了定义的2MB的时候,才会连同清理第1代,然后第1代中活着的部分再升级成第二代...

第二代的容量是没有限制,但是它有动态的阈值(因为等到整个内存空间已满以执行垃圾回收是没有意义的),当达到第二代的阈值后会触发一次0/1/2代完整的垃圾收集。

也就是说,代数越长说明这个对象经历了回收的次数也就越多,那么也就意味着该对象是不容易被清除的。
这种分代的思想来将对象分割成新老对象,进而配对不同的清除条件,这种巧妙的思想避免了直接清理整个堆的尴尬。

67677新澳门手机版 15

.NET Framework4.X覆盖更新

要知道.NET Framework版本目前已经迭代到4.7系列,电脑上明明安装了比4.0更高版本的.NET Framework,然而从文件夹上来看,最高不过4.0,这是为何?
    原来自.NET Framework 4以来的所有.NET Framework版本都是直接在v4.0.30319文件夹上覆盖更新,并且无法安装以前的4.x系列的老版本,所以v4.0.30319这个目录中其实放的是你最后一次更新的NET Framework版本。
.NET Framework覆盖更新:

跨语言和跨平台是什么

跨语言:即只要是面向.NET平台的编程语言((C#、Visual Basic、C /CLI、Eiffel、F#、IronPython、IronRuby、PowerBuilder、Visual COBOL 以及 Windows PowerShell)),用其中一种语言编写的类型可以无缝地用在另一种语言编写的应用程序中的互操作性。
跨平台:一次编译,不需要任何代码修改,应用程序就可以运行在任意有.NET框架实现的平台上,即代码不依赖于操作系统,也不依赖硬件环境。

基于角色的安全性

基于角色的安全机制作为传统的访问控制,其运用的非常广泛,如操作系统的安全策略、数据库的安全策略等等...它的概念就相当于我们经常做的那些RBAC权限管理系统一样,用户关联角色,角色关联权限,权限对应着操作。
整个机制的安全逻辑就和我们平时编写代码判断是一样的,大致可以分为两个步骤.

第一步就是创建一个主体,然后标识这个主体是什么身份(角色) ,第二步就是 身份验证,也就是if判断该身份是否可以这样操作。

而在.NET Framework中,这主体可以是Windows账户,也可以是自定义的标识,通过生成如当前线程或应用程序域使用的主体相关的信息来支持授权。
比如,构造一个代表当前登录账户的主体对象WindowsPrincipal,然后通过 AppDomain.CurrentDomain.SetThreadPrincipal(主体对象);或Thread.CurrentPrincipal的set方法来设置应用程序域或线程的主体对象, 最后使用System.Security.Permissions.PrincipalPermission特性来标记在方法上来进行授权验证。
67677新澳门手机版 1667677新澳门手机版 17

如图,我当前登录账号名称为DemoXiaoZeng,然后通过Thread.CurrentPrincipal设置当前主体,执行aa方法,顺利打印111。如果检测到PrincipalPermission类中的Name属性值不是当前登录账号,那么就报错:对主体权限请求失败。 

67677新澳门手机版 18

在官方文档中有对.NET Framework基于角色的安全性的详细的介绍,感兴趣可以去了解

IntelliTrace智能追溯

还要介绍一点VS的是,其IntelliTrace智能追溯功能,该功能最早存在于VS2010旗舰版,是我用的最舒服的一个功能。

简单介绍,该功能是用来辅助调试的,在调试时可以让开发人员了解并追溯代码所产生的一些事件,并且能够进行回溯以查看应用程序中发生的情形,它是一个非常强大的调试追踪器,它可以捕捉由你代码产生的事件,如异常事件、函数调用(从入口)、ADO.NET的命令(Sql查询语句...)、ASP.NET相关事件、代码发送的HTTP请求、程序集加载卸载事件、文件访问打开关闭事件、Winform/Webform/WPF动作事件、线程事件、环境变量、Console/Trace等输出...

我们可以通过在调试状态下点击调试菜单-窗口-显示诊断工具,或者直接按Ctrl Alt F2来唤起该功能窗口。
67677新澳门手机版 19

当然,VS还有其它强大的功能,我建议大家依次点完 菜单项中的 调试、体系结构、分析这三个大菜单里面的所有项,你会发现VS真是一个强大的IDE。比较实用且方便的功能举几个例子:

比如 从代码生成的序列图,该功能在vs2015之前的版本可以找到( 、)

67677新澳门手机版 20

比如 模块关系的代码图,可以看到各模块间的关系 

67677新澳门手机版 21

比如 对解决方案的代码度量分析结果 

67677新澳门手机版 22

比如 调试状态下 函数调用的 代码图,我们可以看到MVC框架的函数管道模型

67677新澳门手机版 23

以及并行堆栈情况、加载的模块、线程的实际情况

67677新澳门手机版 24

67677新澳门手机版 25

67677新澳门手机版 26

还有如进程、内存、反汇编、寄存器等的功能,这里不再一一展示

  • 非托管的异常
如何在VS中调试.NET Framework源代码

最为关键的是pdb符号文件,没得符号就调不了,对于符号我们从微软的符号服务器上下载(默认就已配置),还得有源代码来调试。

点击工具-选项-调试-常规,如果你之前没有在该配置栏配置过,那么你就勾选 启用源服务器支持 、启用.net Framework源代码单步执行,然后将 要求源文件与原始版本完全匹配 给取消掉。

然后就是下载pdb符号文件了,如果想直接下载那么可以在调试-符号这栏 将Microsoft符号服务器给勾上 。如果想按需下载,那么在调试的时候,可以点击调试-窗口 选择 模块/调用堆栈 来选择自己想加载的去加载。

然后至 网站 点击右上角下载源代码。当你调试代码的时候,会提示你无可用源,这个时候你再将你下载下来的源码文件给浏览查找一下就可以了。

67677新澳门手机版 27

如何配置VS来调试.NET Framework源码: 、

还一种方法是,下载.NET Reflector插件,该插件可以帮助我们在VS中直接调试dll,这种方式操作非常简单,不过该插件收费,具体的可以查看我之前写过的文章(群里有该插件的注册版)

用csc.exe进行编译

现在,我将演示一段文本是如何被csc.exe编译成一个可执行的控制台程序的。
我们新建个记事本,然后将下面代码复制上去。

67677新澳门手机版 2867677新澳门手机版 29

    using System;
    using System.IO;
    using System.Net.Sockets;
    using System.Text;
    class Program
    {
        static void Main()
        {
            string rootDirectory = Environment.CurrentDirectory;
            Console.WriteLine("开始连接,端口号:8090");
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socket.Bind(new System.Net.IPEndPoint(System.Net.IPAddress.Loopback, 8090));
            socket.Listen(30);
            while (true)
            {
                Socket socketClient = socket.Accept();
                Console.WriteLine("新请求");
                byte[] buffer = new byte[4096];
                int length = socketClient.Receive(buffer, 4096, SocketFlags.None);
                string requestStr = Encoding.UTF8.GetString(buffer, 0, length);
                Console.WriteLine(requestStr);
                //
                string[] strs = requestStr.Split(new string[] { "rn" }, StringSplitOptions.None);
                string url = strs[0].Split(' ')[1];

                byte[] statusBytes, headerBytes, bodyBytes;

                if (Path.GetExtension(url) == ".jpg")
                {
                    string status = "HTTP/1.1 200 OKrn";
                    statusBytes = Encoding.UTF8.GetBytes(status);
                    bodyBytes = File.ReadAllBytes(rootDirectory   url);
                    string header = string.Format("Content-Type:image/jpg;rncharset=UTF-8rnContent-Length:{0}rn", bodyBytes.Length);
                    headerBytes = Encoding.UTF8.GetBytes(header);
                }
                else
                {
                    if (url == "/")
                        url = "默认页";
                    string status = "HTTP/1.1 200 OKrn";
                    statusBytes = Encoding.UTF8.GetBytes(status);
                    string body = "<html>"  
                        "<head>"  
                            "<title>socket webServer  -- Login</title>"  
                        "</head>"  
                        "<body>"  
                           "<div style="text-align:center">"  
                               "当前访问"   url  
                           "</div>"  
                        "</body>"  
                    "</html>";
                    bodyBytes = Encoding.UTF8.GetBytes(body);
                    string header = string.Format("Content-Type:text/html;charset=UTF-8rnContent-Length:{0}rn", bodyBytes.Length);
                    headerBytes = Encoding.UTF8.GetBytes(header);
                }
                socketClient.Send(statusBytes);
                socketClient.Send(headerBytes);
                socketClient.Send(new byte[] { (byte)'r', (byte)'n' });
                socketClient.Send(bodyBytes);

                socketClient.Close();
            }
        }
    }

View Code

然后关闭记事本,将之.txt的后缀改为.cs的后缀(后缀是用来标示这个文件是什么类型的文件,并不影响文件的内容)。

上述代码相当于Web中的http.sys伪实现,是建立了通信的socket服务端,并通过while循环来不断的监视获取包的数据实现最基本的监听功能,最终我们将通过csc.exe将该文本文件编译成一个控制台程序。

我已经在前面讲过BCL,基础类库。在这部分代码中,为了完成我想要的功能,我用到了微软已经帮我们实现好了的String数据类型系列类(.NET下的一些数据类型)、Environment类(提供有关当前环境和平台的信息以及操作它们的方法)、Console类(用于控制台输入输出等)、Socket系列类(对tcp协议抽象的接口)、File文件系列类(对文件目录等操作系统资源的一些操作)、Encoding类(字符流的编码)等
这些类,都属于BCL中的一部分,它们存在但不限于mscorlib.dll、System.dll、System.core.dll、System.Data.dll等这些程序集中。
附:不要纠结BCL到底存在于哪些dll中,总之,它是个物理分散,逻辑上的类库总称。

mscorlib.dll和System.dll的区别:

因为我用了这些类,那么按照编程规则我必须在代码中using这些类的命名空间,并通过csc.exe中的 /r:dll路径 命令来为生成的程序集注册元数据表(即以AssemblyRef为代表的程序集引用表)。
而这些代码引用了4个命名空间,但实际上它们只被包含在mscorlib.dll和System.dll中,那么我只需要在编译的时候注册这两个dll的信息就行了。

好,接下来我将通过cmd运行csc.exe编译器,再输入编译命令: csc /out:D:demo.exe D:dicdemo.cs /r:D:dicSystem.dll

/r:是将引用dll中的类型数据注册到程序集中的元数据表中 。
/out:是输出文件的意思,如果没有该命令则默认输出{name}.exe。
使用csc.exe编译生成:
csc编译命令行介绍:

总之,你除了要掌握基本的编译指令外,当你打上这行命令并按回车后,必须满足几个条件,1.是.cs后缀的c#格式文件,2.是 代码语法等检测分析必须正确,3.是 使用的类库必须有出处(引用的dll),当然 因为我是编译为控制台程序,所以还必须得有个静态Main方法入口,以上缺一不可。

可以看出,这段命令我是将 位于D:dic的demo.cs文件给编译成 位于D:名为demo.exe的控制台文件,并且因为在代码中使用到了System.dll,所以还需要通过/r注册该元数据表。
这里得注意为什么没有/r:mscorlib.dll,因为mscorlib.dll地位的特殊,所以csc总是对每个程序集进行mscorlib.dll的注册(自包含引用该dll),因此我们可以不用/r:mscorlib.dll这个引用命令,但为了演示效果我还是决定通过/nostdlib命令来禁止csc默认导入mscorlib.dll文件。

所以,最终命令是这样的: csc D:dicdemo.cs /r:D:dicmscorlib.dll /r:D:dicSystem.dll /nostdlib

67677新澳门手机版 30

因为没有指定输出文件/out选项, 所以会默认输出在与csc同一目录下名为demo.exe的文件。事实上,在csc的命令中,如果你没有指定路径,那么就默认采用在csc.exe的所在目录的相对路径。

67677新澳门手机版 31

而我们可以看到,在该目录下有许多程序集,其中就包含我们需要的System.dll和mscorlib.dll,所以我们完全可以直接/r:mscorlib.dll /r:System.dll

而类似于System.dll、System.Data.dll这样使用非常频繁的程序集,我们其实不用每次编译的时候都去手动/r一下,对于需要重复劳动的编译指令,我们可以将其放在后缀为.rsp的指令文件中,然后在编译时直接调用文件即可执行里面的命令 @ {name}.rsp。

67677新澳门手机版 32

csc.exe默认包含csc.rsp文件,我们可以用/noconfig来禁止默认包含,而csc.rsp里面已经写好了我们会经常用到的指令。
所以,最终我可以这样写 csc D:dicdemo.cs 直接生成控制台应用程序。
67677新澳门手机版 33

本文由67677新澳门手机版发布于计算机编程,转载请注明出处:67677新澳门手机版NET框架基础知识,通俗易懂

关键词: