.NET 体系结构

 

文/玄魂

本文基于.NET 4.0从整体上论述.NET框架体系结构,从新角度对安全相关比较密切地方进行介绍。由于本书性质不同于编程类教程,许多细节问题只能简略概括或者略掉不讲,有疑惑读者还望多多见谅并查找相关资料自行修炼。

本文从.NET安全需要出发,主要介绍公共语言运行库CLR)、公共类型系统(CTS)、公共语言规范(CLS)、中间语言(IL)、框架类库(FCL)、基础类库(BCL)、即时编译(JIT)和预编译,以及动态语言运行时(DLR),从底层进行详细地解析。

1.1公共语言运行时

公共语言运行时Common Language RuntimeCLR)为.NET Framework提供托管运行环境,它负责运行托管代码,进行安全检查,垃圾回收等。本节对运行库进行概述,与安全相关详细内容将会在后续章节进行详细剖析。

微软公司为开发人员开发由CLR负责运行程序创造非常便利条件,开发工具及编译器不断升级,丰富文档详细地介绍.NET开发方方面面。使用基于CLR语言编译器开发代码称为托管代码。托管代码具有许多优点,例如跨语言集成、跨语言异常处理、增强安全性、版本控制和部署支持、简化组件交互模型、调试和分析服务等。

若要使CLR能够向托管代码提供服务,语言编译器必须生成一些元数据来描述代码中类型、成员和引用。元数据与代码一起存储;每个可加载CLR可移植执行 (Portable ExecutablePE) 文件都包含元数据。CLR使用元数据来完成以下任务:查找和加载类、在内存中安排实例、解析方法调用、生成本机代码、强制安全性,以及设置运行时上下文边界。

CLR自动处理对象布局并管理对象引用,当不再使用对象时释放它们。按这种方式实现生存期管理对象称为托管数据。如果编写代码是托管代码,可以在.NET Framework应用程序中使用托管数据、非托管数据,或者同时使用这两种数据。由于语言编译器会提供自己类型(如基元类型),因此可能并不总是知道(或需要知道)这些数据是否是托管

CLR,就可以很容易地设计出对象能够跨语言交互组件和应用程序。也就是说,用不同语言编写对象可以互相通信,并且它们行为可以紧密集成。例如,可以定义一个类,然后使用不同语言从原始类派生出另一个类或调用原始类方法,还可以将一个类实例传递到用不同语言编写另一个类方法。这种跨语言集成之所以成为可能,是因为基于CLR语言编译器和工具使用由CLR定义通用类型系统,而且它们遵循CLR关于定义新类型以及创建、使用、保持和绑定到类型规则。

所有托管组件都带有生成它们所基于组件和资源信息,这些信息构成元数据一部分。CLR使用这些信息确保组件或应用程序具有它所有所需内容指定版本,这样就使代码不太可能由于某些未满足依赖项而发生中断。注册信息和状态数据不再保存在注册表中(因为在注册表中建立和维护这些信息很困难)。取而代之是,有关定义类型(及其依赖项)信息作为元数据与代码存储在一起,这样大大降低组件复制和移除任务复杂性。

语言编译器和工具公开CLR功能方式对于开发人员来说不仅有用,而且很直观。这意味着,CLR某些功能可能在一个环境中比在另一个环境中更突出,对CLR体验取决于所使用语言编译器或工具。

1.2公共类型系统

众所周知,每一种编程语言都有自己类型系统,但稍微接触过不同语言读者都会发现,各种语言类型系统都有许多相同或相似地方。.NET利用各种语言相近特性抽象出完整一套公共类型系统(CTS),使所有类型独立于编写它们源代码语言。CTS构成.NET框架公共语言运行时基础,其中最重要体现就是.NET平台多语言支持,而运行于.NET平台每一种语言又为维护自己语法特色,便使用别名来代替.NET基础数据类型。CTS引入解决许多由多语言协作开发各个模块所带来问题。

1.2.1       CTS基本结构

CTS不仅定义所有数据类型,并提供面向对象模型以及各种语言需要遵守标准。CTS可以分为两个大类:值类型和引用类型,同时这两种类型之间还可以进行强制转换,从值类型到引用类型转换称为Boxing(装箱),从引用类型到值类型转换称为UnBoxing(拆箱)。

CTS基本结构如图1-1所示,CTS每一种类型都是对象,并继承自一个基类System.Object

 

 

1-1 CTS基本结构

1.       值类型和引用类型

值类型(Value Type)直接包含它们数据,值类型实例分配在堆栈。由上图可知,值类型主要包括简单类型、结构体类型和枚举类型等。

引用类型(Reference Type实例分配在托管堆(Managed Heap)上,变量保存实例数据内存引用。由图1-1可知,引用类型可以是自描述类型、指针类型或接口类型。而自描述类型可以进一步细分成数组和类类型。类的类型则可以是用户定义类、装箱值类型和委托。

2.       装箱和拆箱

上文已经提到,所谓“装箱”就是将值类型转换为引用类型,所谓“拆箱”就是将被装箱而成引用类型转换为原来值类型。代码清单1-1演示最简单装箱和拆箱。

 代码清单1-1 装箱和拆箱

using System;
class sample1
http://www.cnblogs.com/Images/dot.gif

public static void Main()  http://www.cnblogs.com/Images/dot.gif

int i=10;  

object obj=i;    

Console.WriteLine(i+","+(int)obj);  

}
}

下面通过Main()方法IL代码来简要分析这段代码中装箱与拆箱,如代码清单1-2所示。关于IL代码更多信息将在1.3节做详细介绍。

代码清单1-2  装箱和拆箱IL代码

.method public hidebysig static void  Main() cil managed

{

  .entrypoint

  // Code size       45 (0x2d)

  .maxstack  3

  .locals init ([0] int32 i,

           [1] object obj)

  IL_0000:  nop

  IL_0001:  ldc.i4.s   10

  IL_0003:  stloc.0

  IL_0004:  ldloc.0

  IL_0005:  box        [mscorlib]System.Int32

  IL_000a:  stloc.1

  IL_000b:  ldloc.0

  IL_000c:  box        [mscorlib]System.Int32

  IL_0011:  ldstr      ","

  IL_0016:  ldloc.1

  IL_0017:  unbox.any  [mscorlib]System.Int32

  IL_001c:  box        [mscorlib]System.Int32

  IL_0021:  call       string [mscorlib]System.String::Concat(object,

                                                              object,

                                                              object)

  IL_0026:  call       void [mscorlib]System.Console::WriteLine(string)

  IL_002b:  nop

  IL_002c:  ret

} // end of method sample1::Main   

 查看装箱和拆箱次数最简单方法就是数一数“box”和“unbox”指令出现次数。可以看出,代码清单1-2中一共执行三次装箱和一次拆箱操作。第一次“object obj=i;”将i装箱;而Console.WriteLine方法用参数是String类型,String是引用类型,因此,“i+","+(int)obj”中,i需要进行一次装箱(转换成String类型)(int)objobj对象拆箱成值类型,而根据WriteLine方法,再次将((int)obj)值类型装箱成引用类型。

装箱和拆箱是有性能损失,因此在通常情况下要尽可能避免装箱和拆箱操作。

1.2.2       公共语言规范

CLR集成很多种语言,并让它们之间可以相互访问,这是因为CLR建立标准类型集、元数据、公共执行环境。但由于各种语言间存在着极大差别(如区分大小写,有不支持unsigned、操作符重载或者参数可变方法),所以要想创建这种让别语言都能访问程序,自己所用编程语言只能使用其语言都支持那些特性。为帮助们更好地做到这一点,Microsoft定义一个“公共语言规范”(Common Language SpecificationCLS)

CLS定义CTS子集,通过定义一组开发人员可以确信在多种语言中都可用功能来增强和确保语言互用性。CLS还建立CLS遵从性要求,这帮助确定托管代码是否符合CLS以及一个给定工具对托管代码(该代码是使用CLS功能)开发支持程度。

 如果组件在对其代码(包括派生类)公开API中只使用 CLS功能,那么可以保证在任何支持 CLS编程语言中都可以访问该组件。遵守CLS规则、仅使用CLS所包含功能组件叫做符合 CLS组件。

如图1-2所示,CLR/CTS提供一个组特性,一些语言会提供这些特性一个较大子集(IL提供全部特性)。而CLS是每种语言必须支持一个最小特性集合。

 

 

1-2 CTSCLS关系

如果一种语言定义一个类型,并希望在另一种语言中使用该类型,就绝对不能在该类型公共和受保护成员中使用CLS外部任何特性。否则其编程人员使用其语言来编写代码时,就可能无法访问该类型成员。代码清单1-3简单地演示遵从CLS兼容性代码编写。

代码清单1-3  CLS兼容性示例

using System;

 //告诉编译器检查CLS相容特性

 [assembly: CLSCompliant(true)]

 namespace SomeLibrary

{

    //开始出现警告,因为类是公有

    public sealed class SomeLibraryType

    {

        //警告,返回值不符合CLS

        public UInt32 Abc()

        {

            return 0;

        }

        //警告,仅大小写不同不符合CLS

        public void abc()

        {

        }

        //没有错误,该方法是私有

        private UInt32 ABC()

        {            return 0;

        }   

}

}

如果将上述代码中SomeLibraryType修饰符public去掉话,一切警告也就消失,因为这样该类将使用默认修饰符internal,所以在程序集外部不可见。

注意  不能把类SomeLibraryType修饰符public改成privateprotectedprotected internal任何一个,因为命名空间中定义元素无法显式声明为 privateprotected protected internal

关于CLS详细内容请读者参考MSDN文档及相关资料。

-------------------------------------注:本文改编自 《.NET 安全揭秘》1.1、1.2节

 

 

posted @ 2012-05-22 23:35  玄魂  阅读(5709)  评论(13编辑  收藏  举报