.NET框架类库概况
构建在.NET框架上所有的软件,都会用到通用语言进行时,即使基于最简单的CLR程序,也需要用到一部分.NET框架类库,更精致复杂的软件则使用这个类库提供的更多服务。
.NET框架类库被组织一套具有层次结构的命名空间,每个命名空间可以包含类型如类和接口,以及其他次级命名空间,整个体系的根名为System
System命名空间
System命名空间是.NET框架类库的最终源头,它除了包含一大套次级命名空间以外,本身也包含很多不同类型,其中最有意思的就是下面这些:
CLR通用类型系统:定义所有的核心类型
Console
Math 这个类提供两打以上的方法用来进行各种数学计算
Environment 用于访问“当前运行之程序”的环境信息
GC 用来影响垃圾回收机制何时发生以及如何发生
System次级命名空间概览
(这一部分借鉴到http://hi.baidu.com/wqk1025/item/2dccf183f2700cc498255fff文章中的一部分)
命名空间 描述
Microsoft.CSharp 支持C#语言编译和生成代码
System 包含了基础类,用于定义类型/数组/字符串/事件/事件处理程序/异常处理/接口/数据类型转换/数学计算/应用程序环境管理等等.
System.Coolections 包含了一组用于管理对象集合(例如列表/队列/数组/哈希表/字典等)的类
System.Data 主要包括了组成ADO.NET体系结构的类
System.diagnostics 提供用于调试/跟踪,以及与系统进程/事件日志/性能计数器进行交互的类
System.Drawing 提供访问GDI+基本图形功能(在System.Drawing以下的命名空间,包括System.Drawing.Drawing2D和System.Drawing.Text等,提供了更高级和更特殊的GDI+图形功能)的类
System.IO 包含了用于读写数据流/文件和普通输入/输出(I/O)功能的类型和类
System.Reflection 包括提供类型检测和动态绑定对象功能的类和接口
System.reflection.Emit 生成动态程序集
System.Text 包含用于字符编码/将字符块转换为字节快/将字节块转换为字符块等功能的对象
System.Text.RegularExpressions 包含了提供访问.NET框架正则表达引擎的类
System.Timer 提供了Timer组件
System.Web 包含了用于实施浏览器/服务器通信和其他Web相关功能的类
System.Web.Services 包含了用于创建和消费Web服务的类
System.Web.UI 包含了用于创建Web页和控件的用户接口的类和接口
System.Windows.Forms 包含了用于创建基于WINDOWS的用户接口的类
System.XML 提供了支持处理XML的类
System.Runtime.Remoting是另一非常重要命名空间,因为它包含你的类型允许访问其他进程记忆其他机器上的托管对象
类库可以分为几大类:
基础服务
用于创建于人们交互的web应用程序的服务
用于处理数据的服务
用以创建分布式程序的服务
基础命名空间
接下来要描述的是.NET技术的基本知识——输入和输出,序列化,反射,事务,以及互操作性——应该纳入每一个.NET程序员开发人员的武器库
输入和输出 System.IO
这类最重要的基础是stream类,该类定义了stream的有用的抽象,它是个字节序列以及如下的一些方法:stream的读取和涂写,stream是一个抽象类,因此有许多特定stream类继承于它,目标是为开发人员提供一致的方法访问各种各样的信息。
序列化System.Runtime.Serilization
对象通常都有对象(state),例如一个类的某个实体可以有一个或者多个字段,每一个都包含某个值,从一个对象中抽取这种状态,无论是将它存储于某地,或者是通过网络传送它,通常都是有意义的,这种抽取的动作称为”讲一个对象序列化”,而反向的处理,从一个被序列化的状态重建一个对象,即使人们广为人知的反序列化,有点混淆的是:序列化这一术语通常同时指定的是做这两件事的能力
.NET框架提供两套不同的格式器,一个是二进制(binaryformatter)实现于命名空间System.Runtime.Serialization.Formatters.Binary的BinaryFormatter class中,将一个对象序列化为一个“直截了当,形式精简,解释(parse)快速”的二进制格式。
二进制格式器以一个简单而精炼的形式吐出状态信息
另一个格式器SOAP,实现于命名空间System.Runtime.Serialization.Formatters.Soap中的Soap Formatter类中,将一个对象序列化为一个SOAP消息
SOAP格式生成同样的信息并包装秤XML格式,而后格式化成为一个SOAP消息
当各时期将某个对象序列化以后,所得结果放入一个流中,所谓流就是字节序列的一个抽象概念,因此可包容任何序列化格式,一旦对象呗存储于一个流中,对象状态就可以被存储于磁盘上(或者被持久化了),或是通过网络发送给其他计算机
对于一个可以被序列化的类型,其设计这必须为其标上Serializable特性
一个被标注以Serializable的类型,当其某个实例被序列化时,可以指明某些字段不被保存-只要为他们设置NonSerializable特性即可
反射 System.Reflection
每一个程序集都包含元数据,手上总有着元数据是很便利的,因为它允许哦我们创建有意义的功能,例如visual studio中的智能感知技术
反射(Reflection)是.NET中的重要机制,通过放射,可以在运行时获得.NET中每一个类型(包括类、结构、委托、接口和枚举等)的成员,包括方法、属性、事件,以及构造函数等。还可以获得每个成员的名称、限定符和参数等。有了反射,即可对每一个类型了如指掌。如果获得了构造函数的信息,即可直接创建对象,即使这个对象的类型在编译时还不知道。
程序代码在编译后生成可执行的应用,我们首先要了解这种可执行应用程序的结构。
应用程序结构分为应用程序域—程序集—模块—类型—成员几个层次,公共语言运行库加载器管理应用程序域,这种管理包括将每个程序集加载到相应的应用程序域以及控制每个程序集中类型层次结构的内存布局。
程序集包含模块,而模块包含类型,类型又包含成员,反射则提供了封装程序集、模块和类型的对象。我们可以使用反射动态地创建类型的实例,将类型绑定到现有对象或从现有对象中获取类型,然后调用类型的方法或访问其字段和属性。反射通常具有以下用途。
(1)使用Assembly定义和加载程序集,加载在程序集清单中列出模块,以及从此程序集中查找类型并创建该类型的实例。
(2)使用Module了解包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。
(3)使用ConstructorInfo了解构造函数的名称、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetConstructors或GetConstructor方法来调用特定的构造函数。
(4)使用MethodInfo了解方法的名称、返回类型、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetMethods或GetMethod方法来调用特定的方法。
(5)使用FiedInfo了解字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值。
(6)使用EventInfo了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。
(7)使用PropertyInfo了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。
(8)使用ParameterInfo了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。
System.Reflection.Emit命名空间的类提供了一种特殊形式的反射,可以在运行时构造类型。
反射也可用于创建称为类型浏览器的应用程序,使用户能够选择类型,然后查看有关选定类型的信息。
XML:System.Xml
XML是eXtensible MarkupLanguage的缩写。
扩展标记语言XML是一种简单的数据存储语言,使用一系列简单的标记描述数据,而这些标记可以用方便的方式建立,虽然XML占用的空间比二进制数据要占用更多的空间,但XML极其简单易于掌握和使用。
XML与Access,Oracle和SQL Server等数据库不同。数据库提供了更强有力的数据存储和分析能力,例如:数据索引、排序、查找、相关一致性等,XML仅仅是展示数据。事实上XML与其他数据表现形式最大的不同是:它极其简单。这是一个看上去有点琐细的优点,但正是这点使XML与众不同。
事务 System.Transaction
事务的思想,即一组操作作为一耳光单元成功或失败,是众多应用程序的基础,这个概念本来很简单,所有操作必须成功,要么全部失败
一、事务的定义
所谓事务,它是一个操作集合,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。典型的例子就像从网上银行系统的帐户A转帐到帐 户B,它经过两个阶段:1. 从帐户A取出款项。2. 把款项放入帐户B中。这两个过程要么同时成功,要么同时失败,这一系列的操作就被称为事务性(Transactional)操作。
在一个事务性操作的环境下,操作有着以下的4种特性,被称为ACID特性
原子性(Atomicity) | 当事务结束,它对所有资源状态的改变都被视为一个操作,这些操作要不同时成功,要不同时失败。 |
一致性(Consistency) | 操作完成后,所有数据必须符合业务规则,否则事务必须中止。 |
隔离性(Isolation) | 事务以相互隔离的方式执行,事务以外的实体无法知道事务过程中的中间状态。 |
持久性(Durable) | 事务提交后,数据必须以一种持久性方式存储起来。 |
二、事务管理器
在软件系统当中可以看到无论在数据库、Web服务、WCF、文件系统都存在着数据参与到事务运作当中,我们把管理这些数据的工具称为资源管理器RM(Resources Manager)。而事务管理器TM(Transaction Manager)就是协调多个资源管理器的工作,保证数据完整性的工具。
由上图可以看到事务的管理流程,系统通知事务管理器TM来启动事务,事务管理器TM控制向多个资源管理器RM并协调RM之间的事务操作。图中存在两个持久化RM,分别管理数据库和文件系统,这些事务操作要不同时成功,要不同时失败。
事务管理器一般分为三类:轻量级事务管理器(LTM)、核心事务管理器(KTM)、分布式事务协调器(DTC)。
1. 轻量级事务管理器 (LTM)
它是包括在 System.Transactions 命名空间内的一个事务管理框架,它只能管理单个应用程序域内的事务。LTM 可以管理多个易变的RM,但只能管理一个持久化RM。若事务试图加入第二个持久化RM,那轻量级事务管理器LTM将提升级别。LTM是性能最高的事务管理 器,在可选择的情况下应该尽可能地使用 LTM 事务管理器。
这里易变RM是指它参与会引发 “未确定状态” 的2PC事务的时候,不需要恢复服务,更多时候,易变RM的数据只存储在内存当中。
而持久化RM是指它参与会引发 “未确定状态” 的2PC事务的时候,它需要恢复服务,持久化RM管理的数据是在于硬盘当中。所以,参与2PC事务的的持久RM必须有新旧两个版本,如果事务引发 “未确定状态” 的时候,那么它就会联系持久化RM,恢复到其中一个版本。
2. 核心事务管理器 (KTM)
KTM是用于WindowsVista和Windows Server 2008 系统中的轻量级事务管理器,与LTM相像,它可以管理多个易变的RM,但只能管理一个持久化RM。
3. 分布式事务协调器(DTC)
分布式事务协调器DTC(Distributed Transaction Coordinator)能管理多个持久化RM中的事务,事务可以跨越应用程序域、进程、硬件、域等所有的边界。在Windows Server 2008当中,DTC支持OleDB、XA、WS-AtomicTransaction、WSCoordination、WS- BusinessActivity等多个协议。由于分布式事务需要在多个参与方之间实现多次通讯,所以是一种巨大的开销,因此,在可以使用LTM和KTM的时候,应该尽量避免使用DTC。在上面图片中的事务同时启动了两个RM分别处理数据库数据与文件数据,当中启动的就是DTC分布式事务。
4. 事务类System.Transactioins.Transaction
Transaction是由Framework 2.0 就开始引入,用于显示管理事务的一个类。通过Transaction可以直接管理事务的中止、释放,也可以获取、克隆当前的环境事务类。
Transaction的公用属性
其中Transaction.Current 比较常用,它可以指向一个当前运行环境中的事务,如果环境事务不存在,系统将返回一个null
Transactiontransaction=Transaction.Current;
属性 | 说明 |
Current | 获取或设置环境事务。 |
IsolationLevel | 获取事务的隔离级别。 |
TransactionInformation | 检索有关某个事务的附加信息。 |
互操作: System.Runtime.InteropService
在.NET框架发布以前,windows开发世界有windows DNA技术一统天下,无论.NET框架多么成功,先于它的windows DNA技术并不会马上消失。
鉴于微软的库克已经对这些应用做了巨大的投资,.NET框架必须提供某种方法,让新程序能够连接它们,同样重要的是。Framework还必须提供一个有效的办法,让托管代码访问现在非com技术创建的DLLs,并调用有底层操作系统提供的本地服务,这额问题的解决方案,由System.Runtime.InteropService命名空间中的类提供。
Windows GUI system.windows.from
这个内容只要做过winfrom的实例就应该比较熟悉
WPF(WindowsPresentation Foundation)是微软推出的基于WindowsVista的用户界面框架,属于NET Framework 3.0的一部分。它提供了统一的编程模型、语言和框架,真正做到了分离界面设计人员与开发人员的工作;同时它提供了全新的多媒体交互用户图形界面。
远程安装windowsform应用程序:ClickOnce
Windows Form界面和浏览器界面哪个更好,答案无疑要视具体点情况而定,浏览器允许访问全世界的web,而且人们都知道怎么用。
Windows Form应用程序当然值得用户,它们的响应速度更快
尽管这样,今天开发的大多数.NET应用程序都是目标与浏览器的,一个重要的原因是来自于部署新版本本地windows应用程序的挑战,如果运行在客户端的程序集发生了任何变化,那么所有客户系统都必须予以更新,比较而言,部署基于浏览器的应用程序的新版本,通常只需要更新程序所运行的服务器即可,如果windows form应用程序安装变得更容易一些,开打人员将少一个构建基于浏览器的软件的理由,并使本地windows应用程序的优点得到最大的发挥。
ClickOnce是.NET Framework 2.0版本中新增的技术,它的存在实在使得windows form也能用程序的部署与升级更容易,ClickOnce应用程序可以从一个web网页,网络上某处的共享文件系统以及本地设备进行安装,一旦安装好,ClickOnce应用程序能够自动检测何时发生了更新,然后只复制和安装哪些自身有变化的部分,若有必要,更新或者整个安装过程也可以回滚
*****************************************************************************无聊的分割线*******************************************************************************************
在.NET框架下,类似C#这样的高级语言经过编译后生成的结果文件被称做程序集,其后缀名是.dll(类库)或.exe(可执行程序)。
程序集的定义只是给编译后生成的文件一个稍微正式一点的名称
Common Intermediate Language,公共中间语言
#源程序在被编译为程序集以后,就独立于C#,因此程序集可以由其他种类的语言所调用;同时,因为程序集并没有包含本地机器的指令,所以它与具体的机器类型也分隔开了,可以被装有.NET框架的任何机器运行。
BCL——基类库
CTS——公共类型系统
CTS规定了可以在语言中定义诸如类、结构、委托等类型,这些规则定义了语言中更高层次的内容。除了定义各种类型外,CTS还规定了各种访问性,比如Private、Public、Family(C#中为Protected)、Assembly(C#中为internal)、Family and assembly(C#中没有提供实现)、Family or assembly(C#中为protected internal)。
CLR——公共语言运行时
CLR不过是一个.NET程序集的运行环境而已,有点类似于Java虚拟机。
程序集概述
载自http://www.cnblogs.com/JimmyZhang/archive/2012/11/27/2790759.html
那么什么样格式的文件才是一个Windows可执行文件?这个格式被称做PE/COFF(Microsoft Windows Portable Executable/Common Object File Format),Windows可移植可执行/通用对象文件格式。Windows操作系统能够加载并运行.dll和.exe是因为它能够理解PE/COFF文件的格式。显然,所有在Windows操作系统上运行的程序都需要符合这个格式,当然也包括.NET程序集在内。在这一级,程序的控制权还属于操作系统,PE/COFF头包含了供操作系统查看和利用的信息。此时,程序集可以表示成如图6-14所示。
图6-14 程序集结构1
在前面提到过,程序集中包含的CIL语言代码并不是计算机可以直接执行的,还需要进行即时编译,那么在对CIL语言代码进行编译前,需要先将编译的环境运行起来,因此PE/COFF头之后的就是CLR头了。CLR头最重要的作用之一就是告诉操作系统这个PE/COFF文件是一个.NET程序集,区别于其他类型的可执行程序。
图6-15 程序集结构2
在CLR头之后就是大家相对熟悉一些的内容了。首先,程序集包含一个清单(manifest),这个清单相当于一个目录,描述了程序集本身的信息,例如程序集标识(名称、版本、文化)、程序集包含的资源(Resources)、组成程序集的文件等。
图6-16 程序集结构3
清单之后就是元数据了。如果说清单描述了程序集自身的信息,那么元数据则描述了程序集所包含的内容。这些内容包括:程序集包含的模块(会在第7章介绍)、类型、类型的成员、类型和类型成员的可见性等。注意,元数据并不包含类型的实现,有点类似于C++中的.h头文件。在.NET中,查看元数据的过程就叫做反射(Reflection)。
图6-17 程序集结构4
接下来就是已经转换为CIL的程序代码了,也就是元数据中类型的实现,包括方法体、字段等,类似于C++中的.cpp文件。
图6-18 程序集结构
注意,图6-18中还多添加了一个资源文件,例如.jpg图片。从这幅图可以看出,程序集是自解释型的(Self-Description),不再需要任何额外的东西,例如注册表,就可以完整地知道程序集的一切信息。
至此对程序集的简单介绍就先到这里,接下来看一下程序集是如何被执行的。
6.6.2 运行程序集
现在已经了解过了程序集,并且知道程序集中包含的CIL代码并不能直接运行,还需要CLR的支持。概括来说,CLR是一个软件层或代理,它管理了.NET程序集的执行,主要包括:管理应用程序域、加载和运行程序集、安全检查、将CIL代码即时编译为机器代码、异常处理、对象析构和垃圾回收等。相对于编译时(Compile time),这些过程发生在程序运行的过程中,因此,将这个软件层命名为了运行时,实际上它本身与时间是没有太大关系的。有一些朋友在初学.NET的时候,纠结在了Runtime这个词上,总以为和时间有什么关系,总是不能很好地理解CLR。笔者认为重要的是理解CLR是做什么的,而不用过于关注它的名称。
实际上,CLR还有一种叫法,即VES(Virtual Execution System,虚拟执行系统)。从上一段的说明来看,这个命名应该更能描述CLR的作用,也不容易引起混淆,但是可能为了和CIL、CTS、CLS等术语保持一致性,最后将其命名为了CLR。在这里,我们知道CLR不过是一个.NET程序集的运行环境而已,有点类似于Java虚拟机。VES这个术语来自于CLI,会在6.7节进行讲述。
可以用图6-19来描述CLR的主要作用。
图6-19 CLR的主要作用
前面已经概要地了解了CLR的作用,接下来开始更进一步的学习。首先遇到的问题就是:CLR以什么样的形式位于什么位置?
由于CLR本身用于管理托管代码,因此它是由非托管代码编写的,并不是一个包含了托管代码的程序集,也不能使用IL DASM进行查看。它位于C:\%SystemRoot%\Microsoft.NET\Framework\版本号下,视安装的机器不同有两个版本,一个是工作站版本的mscorwks.dll,一个是服务器版本的mscorsvr.dll。wks和svr分别代表work station和server。
接下来再看一下CLR是如何运行起来的。虽然从Windows Server 2003开始,.NET框架已经预装在操作系统中,但是它还没有集成为操作系统的一部分。当操作系统尝试打开一个托管程序集(.exe)时,它首先会检查PE头,根据PE头来创建合适的进程。
接下来会进一步检查是否存在CLR头,如果存在,就会立即载入MsCorEE.dll。这个库文件是.NET框架的核心组件之一,注意它也不是一个程序集。MsCorEE.dll位于C:\%SystemRoot%\System32\系统文件夹下所有安装了.NET框架的计算机都会有这个文件。大家可能注意到了,这个库安装在System32系统文件夹下,而没有像其他的核心组件或类库那样按照版本号存放在C:\%SystemRoot%\Microsoft.NET\Framework\文件夹下。这里又存在一个“鸡生蛋问题”:根据不同的程序集信息会加载不同版本的CLR,因此加载CLR的组件就应该只有一个,不能再根据CLR的版本去决定加载CLR的组件的版本。
MsCorEE.dll是一个很细的软件层。加载了MsCorEE.dll之后,会调用其中的_CorExeMain()函数,该函数会加载合适版本的CLR。在CLR运行之后,程序的执行权就交给了CLR。CLR会找到程序的入口点,通常是Main()方法,然后执行它。这里又包含了以下过程:
- 加载类型。在执行Main()方法之前,首先要找到拥有Main()方法的类型并且加载这个类型。CLR中一个名为Class loader(类加载程序)的组件负责这项工作。它会从GAC、配置文件、程序集元数据中寻找这个类型,然后将它的类型信息加载到内存中的数据结构中。在Class loader找到并加载完这个类型之后,它的类型信息会被缓存起来,这样就无需再次进行相同的过程。在加载这个类以后,还会为它的每个方法插入一个存根(stub)。
- 验证。在CLR中,还存在一个验证程序(verifier),该验证程序的工作是在运行时确保代码是类型安全的。它主要校验两个方面,一个是元数据是正确的,一个是CIL代码必须是类型安全的,类型的签名必须正确。
- 即时编译。这一步就是将托管的CIL代码编译为可以执行的机器代码的过程,由CLR的即时编译器(JIT Complier)完成。即时编译只有在方法的第一次调用时发生。回想一下,类型加载程序会为每个方法插入一个存根。在调用方法时,CLR会检查方法的存根,如果存根为空,则执行JIT编译过程,并将该方法被编译后的本地机器代码地址写入到方法存根中。当第二次对同一方法进行调用时,会再次检查这个存根,如果发现其保存了本地机器代码的地址,则直接跳转到本地机器代码进行执行,无需再次进行JIT编译。
可以看出,采用这种架构的一个好处就是,.NET程序集可以运行在任何平台上,不管是Windows、UNIX,还是其他操作系统,只要这个平台拥有针对于该操作系统的.NET框架就可以运行.NET程序集。