对.Net Framework的认识(1)
.Net Framework主要有两部分组成,CLR+类库。
3C: CTS(Common Type System), CLS(Common Language Specification), CLR(Common Language Runtime)。
CTS是个类型标准,任何以.NET平台作为目标的语言必须建立它的数据类型与CTS的类型间的映射,从而使符合CTS的各种语言之间可以无缝互操作,CTS还提供了语言间的继承,例如C#类继承VB.Net类。
CLS制定了一种以.NET平台为目标的语言所必须支持的最小特征,以及该语言与其他.NET语言之间实现互操作性所需要的完备特征。
CLR负责安全地载入和运行用户程序代码,包括对不用对象的垃圾回收和安全检查。在CLR监控之下运行的代码,称为托管代码(managed code)。
CLS是CTS的子集,CLR是CTS的具体实现。
.Net平台结构图:
CLR结构图:
结构图中的最底下两层也叫做CLI(Common Language Infrastructure)。
符合CTS的语言经过自己的编译器编译后会生成托管模块,托管模块合并成程序集后在CLR上运行。
有些语言一部分符合CTS(也就是托管代码),一部分不符合(也就是非托管代码),那么,符合的部分就会被编译成托管模块,不符合的则直接编译成机器码(native code)。
托管模块是个PE文件(Portable Executable,可移植可执行)包括
1、CLR头:描述了CLR版本(包括Major和Minor),PE文件入口(是元数据中方法定义表或者文件定义表的入口引用,如果是可执行文件必须有入口点,如果是非可执行文件,若是纯IL,可以没有入口点,如果嵌入了机器码,则必须把DLLMain作为入口点函数),资源(包括托管资源和非托管资源,IL汇编器在每个托管可执行文件中只能嵌入一个资源文件),文件是否受到强名称签名保护,文件里是否嵌入了机器码,是在32位机器上还是64位机器上运行,保存非托管代码的虚函数的表的大小和地址,元数据引用。
CLR头结构图:
2、元数据
3、IL(Intermediate Language)中间码,在需要时被CLR动态读取并由JIT编译成机器码执行。
.Net应用程序结构分成应用程序域,程序集,模块(托管模块),类型,成员这几个层次,CLR加载管理应用程序域,包括把程序集加载到相应的应用程序域里并控制程序集中的内存布局。
强名称签名(strong name siganature)是用来认证程序集(Assembly),作为程序集的唯一性标识,并且防止被他人篡改冒充你发布的程序集。强名称签名包括没有扩展名的程序集名,版本号,语言化标志,公钥,以及用发布者的私钥进行签名。
元数据是由各种表和各种堆流(Heap Stream)构成。
元数据中的表分为三类,定义表,引用表和清单表。定义表描述了定义的内容;引用表描述了引用的内容;清单表描述了构成Assembly的文件,由Assembly中的文件实现的公共导出类型,与Assembly相关联的资源和数据文件。CLR会先加载清单表,然后根据清单表里的内容加载其他文件。
元数据中的表有很多有父子关系,为了优化这些关系并提供定位效率,父数据和子数据都会排序,父数据一般指向子数据的第一个,而子数据的最后一个是下一个父数据的第一个子数据的上一个,如图所示:
元数据中的堆流,分为6种,如图所示:
其中,
#Strings:用来存储元数据项的名字,如类名、方法名等 //Heap Stream
#Blob: 用来存储一些内部的对象实例,如默认值什么的 //Heap Stream
#US: 用户定义的字符串常量 //Heap Stream
#GUID: 包含各种全局统一标志符 //Heap Stream
#~: “优化的”、“压缩的”元数据,里面的元数据表以优化方式存储(我们在父子关系中刚刚提到过的) //Table Stream
#-: 非优化的元数据(和#~不能共存) //Table Stream
其中(#~或#-)、#GUID、#Strings是必不可少的。
操作元数据可以通过三种方法:
1、.Net的反射类库
2、微软提供的Metadata Unmanaged API
3、二进制层面的逆向工程分析
元数据的用途:
1、方便CLR定位执行IL代码。
2、方便各语言之间的交互操作。
3、验证代码,确保执行安全操作。
4、正反序列化。
5、GC跟踪对象生存期以及对象类型。
程序集:一个或多个托管模块/资源文件的逻辑分组,是最小的重用,安全性及版本控制单元。根据编译工具设置可以生成单文件程序集也可以生成多文件程序集。
CTS的一些规定:
1、一个类型可以包含0或多个成员
2、类型可视化及类型成员访问规则
3、定义继承、虚方法和对象生成期的管理规则
4、所有类型必须继承自System.Object
使用[assembly:CLSComplaint(true)]标志程序集(必须放在namespace外),则编译器会检查程序集的CLS兼容性。对于不符合CLS语法的方法,在另一个语言里是无法看到它的。
COM:Component Object Model,由dll和exe组成。
与COM的互操作:
1、托管代码可以调用DLL中的非托管函数
2、托管代码可以使用现成的COM组件
3、非托管代码可以使用托管类型
CUI:Console User Interface,控制台程序
GUI:Graphics User Interface,窗口程序
创建强命名程序集步骤:
1、生成公钥/私钥对,使用SN命令,该命令大小写敏感
如:SN -k MyKey.keys
MyKey.keys是用来保存生成的公钥/私钥对的文件
2、将原有程序集变成强命名程序集
如:csc /keyfile:MyKey.keys app.cs
这里的app.cs必须是包含清单表的文件,不能对不包含清单表的文件签名。编译器会打开保存公钥/私钥对的文件用私钥签名并把公钥嵌入清单表中。其中,用私钥对文件签名是指程序集的FileDef清单中列出的文件的内容被私钥进行哈希处理并与文件名一起存入FileDef中。
GAC(Global Assembly Cache):用于缓存许多应用程序需要用到的dll,避免把这些dll都拷贝一份到应用程序的执行目录下,比如System.Data。可以通过GACUtil /i sample.dll把自己的程序集安装到GAC里,也可以通过GACUtil /u sample.dll把自己的程序集移出GAC,安装到GAC的程序集必须是强签名的。拷贝GAC目录中的dll,可以通过cmd先到C:\WINDOWS\assembly下,然后根据需要的dll到达它所在的目录,然后用copy命令拷贝到其他文件夹下。由于GAC需要运行命令来安装及卸载并且获取它里面的dll比较麻烦,所以它不属于简单复制式部署。另外GAC安装还需要一定级别的系统权限才行。
is和as关键字:is用于匹配类型是否正确,as用于转换类型,都不会抛出异常,as转换失败就赋null值。
在C#中string和String的区别在于string是C#映射String的,而String是.Net Framework自带的类。
值类型是在stack中分配内存,引用则是在heap中分配。struct和enum都是值类型。装箱就是把值类型变成引用类型,拆箱就是把引用类型变成值类型(当然前提是要能够变成),建议值类型最好别装箱操作,因为影响效率。另外值类型不受GC(垃圾回收机制)影响,因为它是分配在stack里的。