理解.NET中的CLR原理(一)
首先本文是的目的是要让大家明白,CLR在。NET中的作用是十分重要的,如果要完全掌握。NET那么理解CLR运行原理就是必然的事情。比如,你想在程序中实现动态获取程序集信息,动态创建,后期绑定,反射等特性,那么正确的理解CLR的原理就显得格外的重要。
下面让我来做一个整体的介绍,来帮助你来了解CLR中的一些另人激动的特性。理解这些特性将更好的帮助你来理解CLR。
*与本机代码无关 ——— MSIL (中间语言)
*让我们使用同一种语言 ——— CLR (公共语言运行时)
*我们手中的零件 ——— Assembly (装配件)
*让我们在同一个系统中运行 ——— CTS (通用类型系统)
*宇宙大爆炸后的产物 ——— metadata (元数据)
*让我们的语言可以交流 ——— CLS (公共语言系统)
*在动态中交互 ——— Reflection (反射)
*属于我们自己的空间 ——— NameSpace (名称空间)
本文的目的力求做到精简,要精简,就不得不省去细节,但为了全面和完整我会在文中放连接,那样,可以让读者对自己感兴趣或希望有更多了解的地方进行深入的了解。(注:连接有部分为MSDN中内容,因此你必须要安装。NET 的合并MSDN才可查看。)
词汇:
由于我想简写,因此会直接采用原词。但为了帮助理解,我在这里给出翻译:
MSIL:微软中间语言
Reflection:反射
Metadata:元数据
PE:可执行可移植文件
Assembly: 程序集(装配件)
NameSpace:名称空间
CTS:通用类型系统
GC(Garbage Colection) :无用单元回收
CLR:公共语言系统
Attribute:属性(注意不要和Property混淆)
Boxing: 装箱
UnBoxing: 拆箱*与本机代码无关 ——— MSIL (中间语言)1.谈谈MSIL
图例:
<¡xml:namespace prefix = v />
平台无关本机代码
MSIL
使用。NET支持的语言所编写的代码
JIT
编译
<¡xml:namespace prefix = w/>
MSIL(MicrosoftIntermediate Language)微软的中间语言。和JAVA的虚拟机类似,是与CPU无关的指令集。当编译为托管代码时,编译器将源代码翻译为MSIL,
如上图所示。MSIL 包括用于加载、存储和初始化对象以及对对象调用方法的指令,还包括用于算术和逻辑运算、控制流、直接内存访问、异常处理和其他操作的指令。在可以执行代码前,必须将 MSIL 转换为 CPU 特定的代码,这通常是通过实时 (JIT) 编译器完成的。由于公共语言运行库为它支持的每种计算机结构都提供了一种或多种 JIT 编译器,因此可以在任何受支持的结构上对同一组 MSIL 进行 JIT 编译和执行。这样总结上面的就是:中间语言是一组独立于CPU的指令集,它可以被即时编译器Jitter翻译成目标平台的本地代码。
2.谈谈PE
Windows PE和一个 .NETPE的主要区别在于Windows PE 是由操作系统执行的,而.NET PE 却被转变成为.NET Framework的CLR. 识别一个PE是 .NET还是Windows取决于他的通用的目标文件格式 (COFF) 是否使用Windows的操作系统. 目标文件格式 (COFF) 指定了任何文件都分成两个部分:文件数据本身以及描述文件内包含的数据内容的头文件串。
MSIL 汇编程序从 MSIL 汇编语言生成可移植可执行的 (PE) 文件。可以运行结果可执行文件(该文件包含 MSIL 和所需的元数据)以确定 MSIL 是否按预期执行。这就是我为什么会谈到PE。
。NET中的PE参考:
http://www.csdn.net/Develop/article/13%5C13683.shtm
。NET中的PE
.NETPE File Format
COFF and PE Headers
Code and Data Sections
Metadata Tables and IL Organization
Java Class File Format vs. .NET PE File For
那么PE文件是怎么执行的呢?下面是一个典型的.NET应用程序的执行过程:
1. 用户执行编译器输出的应用程序(PE文件),操作系统载入PE文件,以及其他的DLL(.NET动态连接库)。
2. 操作系统装载器根据PE文件中的可执行文件头跳转到程序的入口点。显然,操作系统并不能执行中间语言,该入口点也被设计为跳转到mscoree.dll(.NET平台的核心支持DLL)的_ CorExeMain()函数入口。
3. CorExeMain()函数开始执行PE文件中的中间语言代码。这里的执行的意思是CRL(通用语言运行时)按照调用的对象方法为单位,用JIT(即时编译器)将中间语言编译成本地机二进制代码,执行并根据需要存于机器缓存。
4. 程序的执行过程中,GC(垃圾收集器)负责内存的分配,释放等管理功能。
5. 程序执行完毕,操作系统卸载应用程序。
3. 工具参考:
MSIL 反汇编程序是 MSIL 汇编程序 (Ilasm.exe) 的伙伴工具。Ildasm.exe 采用包含 Microsoft 中间语言 (MSIL) 代码的可移植可执行 (PE) 文件,并创建相应的文本文件作为 Ilasm.exe 的输入。
ms-help://MS.VSCC/MS.MSDNVS.2052/cptools/html/cpconmsildisassemblerildasmexe.htm
*让我们使用同一种语言 ——— CLR (公共语言运行时)1.CLR的特性:
1.跨语言应用
当编程人员在用自己喜欢的编程语言写源代码的时候, 这个源代码在被转化成媒介语言(IL)之前,先被编译成了一个独立的可执行单元(PE)。这样无论
你是一个VB。NET程序员,或一个C#程序员,甚至是使用托管的C++的程序员。只要被编译成IL就是同等的。
首先,编译输出的exe是一个由中间语言(IL),元数据(Metadata)和一个额外的被编译器添加的目标平台的标准可执行文件头(比如Win32平台就是加了一个标准Win32可执行文件头)组成的PE(portable executable,可移植执行体)文件,而不是传统的二进制可执行文件--虽然他们有着相同的扩展名。中间语言是一组独立于CPU的指令集,它可以被即时编译器Jitter翻译成目标平台的本地代码。中间语言代码使得所有Microsoft.NET平台的高级语言C#,VB.NET,VC.NET等得以平台独立,以及语言之间实现互操作。元数据是一个内嵌于PE文件的表的集合。
2.安全性
。NET 提供了一组安全方案。负责进行代码的访问安全性检查。允许我们对保护资源和操作的访问。代码需要经过身份确认和出处鉴别后才能得到不同程度的信任。安全策略是一组可配置的规则,公共语言运行库在决定允许代码执行的操作时遵循此规则。安全策略由管理员设置,并由运行库强制。运行库确保代码只能访问安全策略允许的资源和调用安全策略允许的代码。
每当发生加载程序集的尝试时,运行库就使用安全策略确定授予程序集的权限。在检查了描述程序集标识的信息(称为证据)后,运行库使用安全策略决定代码的信任程度和由此授予程序集的权限。证据包括但不仅限于代码的出版商、它的站点以及它的区域。安全策略还确定授予应用程序域的权限。
安全性参考:
ms-help://MS.VSCC/MS.MSDNVS.2052/cpguide/html/cpconkeyconceptsinsecurity.htm
3.版本化和程序
由于使用了元数据,所以你可以使用XCOPY 简单的复制就可以了,而CLR也可以在运行时期读取元数据,以确保多版本程序运行在同一进程中。使用公共语言运行库的程序集的所有版本控制都在程序集级别上进行。一个程序集的特定版本和依赖程序集的版本在该程序集的清单中记录下来。除非被配置文件(应用程序配置文件、发行者策略文件和计算机的管理员配置文件)中的显式版本策略重写,否则运行库的默认版本策略是,应用程序只与它们生成和测试时所用的程序集版本一起运行。
参考程序集版本控制:
ms-help://MS.VSCC/MS.MSDNVS.2052/cpguide/html/cpconassemblyversioning.htm
4.调用和配置
当运行库试图解析对另一个程序集的引用时,就开始进行定位并绑定到程序集的进程。该引用可以是静态的,也可以是动态的。在生成时,编译器在程序集清单的元数据中记录静态引用。动态引用是由于调用各种方法而动态构造的,例如 System.Reflection.Assembly.Load 方法。
引用程序集的首选方式就是使用完全引用,包括程序集名称、版本、区域性和公钥标记(如果存在)。运行库就会使用这些信息来定位程序集。无论引用是对静态程序集的引用还是对动态程序集的引用,运行库均使用相同的解析过程。
还可通过向调用方法仅提供有关程序集的部分信息的方式(例如仅指定程序集名称),对程序集进行动态引用。在这种情况下,仅在应用程序目录下搜索程序集,不进行其他检查。您可以使用不同加载程序集方法中的任何方法(例如 System.Reflection.Assembly.Load 或 AppDomain.Load)进行部分引用。如果希望运行库在全局程序集缓存和应用程序目录下检查引用的程序集,可以用 System.Reflection.Assembly.LoadWithPartialName 方法指定部分引用。
最后,可以使用诸如System.Reflection.Assembly.Load 之类的方法进行动态引用并只提供部分信息;然后在应用程序配置文件中用
5.GC
一个跟踪过程,它传递性地跟踪指向当前使用的对象的所有指针,以便找到可以引用的所有对象,然后重新使用在此跟踪过程中未找到的任何堆内存。公共语言运行库垃圾回收器还压缩使用中的内存,以缩小堆所需要的工作空间。
垃圾收集器的基本算法很简单:
● 将所有的托管内存标记为垃圾
● 寻找正被使用的内存块,并将他们标记为有效
● 释放所有没有被使用的内存块
● 整理堆以减少碎片
GC参考:
http://www.csdn.net/Develop/Read_Article.asp¡Id=13895
http://www.csdn.net/Develop/article/13%5C13896.shtm
6.。NETFrameWork
不用我多说,用过的都知道。参考:
ms-help://MS.VSCC/MS.MSDNVS.2052/cpref/html/cpre
理解.NET中的CLR原理(二)
理解.NET中的CLR原理
*我们手中的零件 ——— Assembly (装配件)1.关于装配件*让我们在同一个系统中运行 ——— CTS (通用类型系统)1.先来谈谈System.Object ——— 一切NET的根源
CTS中的所有类全部都是从System.Object对象派生而来。它提供了基本的操作如下:
已重载。确定两个 Object 实例是否相等。
用作特定类型的哈希函数,适合在哈希算法和数据结构(如哈希表)中使用。
获取当前实例的。
确定指定的 Object 实例是否是相同的实例。
返回表示当前 Object 的。
另外CTS还定义了一组跨语言的对象。大多数编译器都提供这些对象的别名。这里以C#为例说明:
object ,int ,string,sbyte,byte,short,long,float,char,double,bool decimal.
以下两个论题,由于我在论坛中答了太多遍,如果你有兴趣可以去CSDN中C#版去搜索,会找到很多,基本我都参与了。因此,这里我只放连接。
2.值类型和引用类型
引用类型参考:
ms-help://MS.VSCC/MS.MSDNVS.2052/csref/html/vcrefreferencetypes.htm
值类型参考:
ms-help://MS.VSCC/MS.MSDNVS.2052/csref/html/vcrefvaluetypes.htm
3.不要把boxing and unboxing 和类型转换混淆
参考:
ms-help://MS.VSCC/MS.MSDNVS.2052/csref/html/vclrfboxingunboxingpg.htm
*宇宙大爆炸后的产物 ——— metadata (元数据)1. metadata和IDL的关系:
元数据描述了代码中的数据类型等一些通用语言运行时(Common Language Runtime)需要在代码执行时知道的信息。元数据使得.NET应用程序代码具备自描述特性,提供了类型安全保障,这在以前需要额外的类型库或接口定义语言(Interface Definition Language,简称IDL)。如果,你以前有做过COM那么你一定知道,在COM要获取Metadata必须要从注册表中访问。
元数据是组件对象模型中关于PE的信息, 元数据之间是通过非标准类型的库通信的. 在.NET中,这些数据包含在目标文件格式中,包括与目标文件格式相匹配的PE以及某些确定的指导方针;他所包含的信息像汇编的名字,版本号,语言外部的形式用作参考注释, 内部形式也很透明,清楚的阐明了使用的方法,引用的函数,类等等。
通用计算机语言(CLR)使用元数据是有专用目的的。安全性是由一个公用的秘钥在PE的头文件中管理的. CLR可以知道一些关于类和模块的信息, 如果需要的话甚至结构的信息也可以掌握。
CLR中类装载器的组件也使用元数据载汇编中定位特殊的类,本地的或者网络间的均可。 Just-in-time (JIT) 也使用了元数据来把媒介语言(IL)转换成可执行的代码。一些其他的程序也同样在使用元数据时受益匪浅。这里有一个很普遍的在Windows 2000下使用Word 文档的例子。如果这个文档文件已经完成了内容,作者,标题,或者其他的一些元数据, 他的文本内容会像工具注释一样的显示出来,当使用者挥动着鼠标到这个文本文件图标的上方时。你可以使用Ildasm.exe 的功能来看PE库中的元数据。
2.从Attribute开始:
元数据的产生就是从Attribute开始,的,与COM不同的是。NET中的属性可以自己定义。所有。NET中的Attribute均是从System.Attribute派生的。那么我们就可以创建自己的描述信息。加以管理,如下;
AttributeUsage(AttributeTargets.All)] public class YarshrayAttribute :System.Attribute { private stringname; private int age; private bool sex; public DeveloperAttribute(string name,int age,bool sex) { this.Name = name; this.Age = age; this.Sex = sex; } public virtual string Name { get {returnname;} } publicvirtual int Age { get {return age;} } public virtual bool Sex { get {return sex;} set {sex = value;} } }[YarshrayAttribute(“yarshray”,”21”,Sex=true;] public class YarshrayTest { public Yarshray() { //nothing just make a test } }
下面我们来使用上面的元数据:
Using System.Relection;
Using System;
Namespace yarshrayAPP{
public class yarshrayConsoleApp
{
public static void <¡xml:namespace prefix = st1 ns ="urn:schemas-microsoft-com:office:smarttags" />()
{
MemberInfor om;
om=typeof(YarshrayTest);
object o;
o=om.GetCustomAttribute(typeof(Yarshray),true);
YarshrayAttribute ya=( YarshrayAttribute)o;
Console.WriteLine(“name:”+ys.Name);
Console.WriteLine(“Age:”+ys.Age);
Console.WriteLine(“Sex:”+ys.Sex);
}
}
理解.NET中的CLR原理(三)
理解.NET中的 CLR原理*让我们的语言可以交流 ——— CLS (公共语言系统)1.统一标准的制定:
了帮助创建兼容 .NET Framework 的语言,Microsoft 设计了公共语言规范 (CLS)。每种语言要使用 .NET Framework 和公共语言运行库,以及与用其他语言编写的组件进行互操作,就必须提供 CLS 所描述的功能。如果一种语言实现了必需的功能,那么它就被称为兼容 .NET。每种兼容 .NET 的语言都支持相同的数据类型,使用相同的 .NET Framework 类,编译为相同的 MSIL,并且使用相同的公共语言运行库管理执行过程。因此,各种兼容 .NET 的语言之间没有优劣之别。开发人员可以自由地为特定的组件选择最好的语言同时又不损失该平台的能力和自由度。另外,使用一种语言编写的组件也能够很容易地与用另外一种语言编写的组件进行互操作。例如,您可以用 C# 编写一个从用 Visual Basic 编写的基类继承而来的类。CLS 已经被提交给 ECMA 进行标准化,这鼓励许多语言开发人员为许多语言创建兼容 .NET 的版本。在撰写本文时有 20 多种兼容 .NET 的语言正在开发中。<¡xml:namespace prefix = o />
MSDN是我觉得比较全面的一个介绍:
ms-help://MS.VSCC/MS.MSDNVS.2052/cpguide/html/cpconwhatiscommonlanguagespecification.htm
*在动态中交互 ——— Reflection (反射)
CLS加载器管理应用程序域。这种管理包括将每个程序集加载到相应的应用程序域以及控制每个程序集中类型层次结构的内存布局。
Assembly包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。反射通常具有以下用途:
· 使用 Assembly 定义和加载程序集,加载在程序集清单中列出的模块,以及从此程序集中查找类型并创建该类型的实例。
· 使用 Module 了解如下的类似信息:包含模块的程序集以及模块中的类等。您还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。
· 使用 ConstructorInfo了解如下的类似信息:构造函数的名称、参数、访问修饰符(如 public 或 private)和实现详细信息(如 abstract 或 virtual)等。使用 Type 对象的 GetConstructors或 GetConstructor 方法来调用特定的构造函数。
· 使用 MethodInfo 来了解如下的类似信息:方法的名称、返回类型、参数、访问修饰符(如 public 或 private)和实现详细信息(如 abstract 或 virtual)等。使用 Type 对象的 GetMethods 或 GetMethod 方法来调用特定的方法。
· 使用 FieldInfo 来了解如下的类似信息:字段的名称、访问修饰符(如 public 或 private)和实现详细信息(如 static)等;并获取或设置字段值。
· 使用 EventInfo 来了解如下的类似信息:事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等;并添加或移除事件处理程序。
· 使用 PropertyInfo 来了解如下的类似信息:属性的名称、数据类型、声明类型、反射类型和只读或可写状态等;并获取或设置属性值。
· 使用 ParameterInfo 来了解如下的类似信息:参数的名称、数据类型、参数是输入参数还是输出参数,以及参数在方法签名中的位置等。
System.Reflection.Emit命名空间的类提供了一种特殊形式的反射,使您能够在运行时构造类型。
反射也可用于创建称作类型浏览器的应用程序,它使用户能够选择类型,然后查看有关选定类型的信息。
反射还有其他一些用途。JScript 等语言编译器使用反射来构造符号表。System.Runtime.Serialization 命名空间中的类使用反射来访问数据并确定要持久保存的字段。System.Runtime.Remoting命名空间中的类通过序列化来间接地使用反射。
1.从Reflection API开始
参考System.Reflection 命名空间
ms-help://MS.VSCC/MS.MSDNVS.2052/cpref/html/frlrfSystemReflection.htm
2.动态创建Assembly
下面我做一个事例代码首先我下一个代码然后在用动态查看信息:
using System;
public classSayName {
private const String SayName = "I’m Yarshray";
publicSayMyName() {
}
public void OutPutName() {
Console.WriteLine(SayName);
}
}
using System;
usingSystem.Reflection;
public classYarshrayReflection {
public static void Main() {
Assembly as = Assembly.Load("SayMyName");
Type t = as .GetType("SayMyName");
MethodInfo mi = t.GetMethod("OutPutName");
Object o = Activator.CreateInstance(t);
mi.Invoke(o);
}
}
*属于我们自己的空间 ——— NameSpace (名称空间)1.也谈名称空间:
关于名称空间我想不用我多说。它相当一个可以包含类的文件夹。但要注意一点,尽量把相关的类放到同一个名称空间中。因为那样比较方便维护。
如:
namespaceYarshrayTools
{
using System;
使用namespace可以方便的定义名称空间,上面我定义了一个名为YarshrayTools的名称空间,using可以很方面的引入名称空间
public classYarshrayClass
{
这样就把类放入了名称空间中。引用名称空间中的类的方法如下:
YarshrayTools.YarshrayClassys=new YarshrayTools.YarshrayClass();
你也许,注意到了,我使用的是带名称空间的引用。这种方式叫做 Full Qunalfied Name(完全限定名),当然,如果你在代码中usingYarshrayTools
那么就不需要这样做了
理解.NET中的CLR原理(四)
附录:。NET框架工具介绍
工具
说明
允许使用 Windows 资源管理器查看和操作全局程序集缓存的内容。
从一个或多个文件(资源文件或 Microsoft 中间语言 (MSIL) 文件)生成一个带有程序集清单的文件。
读取程序集内的元数据并向注册表添加必要的项,以便使 COM 客户端能够透明地创建 .NET 框架类。
显示失败的程序集绑定的详细信息。这些信息有助于您诊断 .NET 框架无法在运行时找到程序集的原因。
允许查看和操作全局程序集缓存和下载缓存的内容。由于 Shfusion.dll 提供了类似的功能,所以可以从构造脚本、生成文件文件和批处理文件使用 Gacutil.exe。
允许通过执行指定程序集的安装程序组件,安装和卸载服务器资源。
为当前登录的用户列出或删除所有的现有存储区。
从托管程序集创建本机映像,并将其安装在本地计算机的本机映像缓存中。
.NET 框架配置工具 (Mscorcfg.msc)
提供图形界面以管理 .NET 框架安全策略和使用远程处理服务的应用程序。此工具还允许管理和配置全局程序集缓存中的程序集。
通过加载、注册程序集并将类型库生成、注册和安装到现有的 COM+ 1.0 应用程序中,将托管类添加到 Windows 2000 组件服务中。
使用一种称为“远程处理”的技术帮助您编译与 XML Web services 进行通信的客户端应用程序。
从公共语言运行库程序集生成类型库。
将 COM 类型库中发现的类型定义转换成托管元数据格式的等同定义。
从 Web 服务描述语言 (WSDL) 协定文件、XML 架构定义 (XSD) 架构文件和 .discomap 发现文档为 XML Webservices 和 XML Web services 客户端生成代码。
发现位于 Web 服务器上的 XML Webservices 的 URL,并将与每个 XML Webservices 相关的文档保存到本地磁盘上。
生成的 XML 架构应遵从 WWW 联合会 (W3C) 提出的 XSD 语言。此工具可生成公共语言运行库类和 XSD 架构文件的 DataSet 类。
调试工具
工具
说明
Microsoft CLR 调试器 (DbgCLR.exe)
使用图形界面提供调试服务,以帮助应用程序开发人员查找和修复针对运行库的各类程序中的错误。
使用公共语言运行库“调试 API”提供命令行调试服务。用于查找和修复针对运行库的各类程序中的错误。
推荐文章参考 :