公共语言运行库--NET编程基础

本文介绍了.NET Framework的核心组件——公共语言运行库的功能和服务,包括自动内存管理、程序集的概念及元数据的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

介绍

  
 .NET Framework 提供了一个称为公共语言运行库的运行时环境,它运行代码并提供使开发过程更轻松的服务。
    公共语言运行库的功能通过编译器和工具公开,您可以编写利用此托管执行环境的代码。使用基于公共语言运行库的语言编译器开发的代码称为托管代码;   
    托管代码具有许多优点,例如:跨语言集成、跨语言异常处理、增强的安全性、版本控制和部署支持、简化的组件交互模型、调试和分析服务等。
    若要使公共语言运行库能够向托管代码提供服务,语言编译器必须生成一些元数据来描述代码中的类型、成员和引用。
    元数据与代码一起存储;每个可加载的公共语言运行库可移植执行 (PE) 文件都包含元数据。
    公共语言运行库使用元数据来完成以下任务:查找和加载类,在内存中安排实例,解析方法调用,生成本机代码,强制安全性,以及设置运行时上下文边界。
    公共语言运行库自动处理对象布局并管理对象引用,当不再使用对象时释放它们。按这种方式实现生存期管理的对象称为托管数据。
    垃圾回收消除了内存泄漏以及其他一些常见的编程错误。如果您编写的代码是托管代码,则可以在 .NET Framework 应用程序中使用托管数据、非托管数据或者同时使用这两种数据。
    由于语言编译器会提供自己的类型(如基元类型),因此您可能并不总是知道(或需要知道)这些数据是否是托管的。
    有了公共语言运行库,就可以很容易地设计出对象能够跨语言交互的组件和应用程序。
    也就是说,用不同语言编写的对象可以互相通信,并且它们的行为可以紧密集成。
    例如,可以定义一个类,然后使用不同的语言从原始类派生出另一个类或调用原始类的方法。
    还可以将一个类的实例传递到用不同的语言编写的另一个类的方法。
    这种跨语言集成之所以成为可能,是因为基于公共语言运行库的语言编译器和工具使用由公共语言运行库定义的通用类型系统,
    而且它们遵循公共语言运行库关于定义新类型以及创建、使用、保持和绑定到类型的规则。
    所有托管组件都带有生成它们所基于的组件和资源的信息,这些信息构成了元数据的一部分。
    公共语言运行库使用这些信息确保组件或应用程序具有它需要的所有内容的指定版本,这样就使代码不太可能由于某些未满足的依赖项而发生中断。
    注册信息和状态数据不再保存在注册表中(因为在注册表中建立和维护这些信息很困难)。
    取而代之的是,有关您定义的类型(及其依赖项)的信息作为元数据与代码存储在一起,这样大大降低了组件复制和移除任务的复杂性。
    语言编译器和工具公开公共语言运行库的功能的方式对于开发人员来说不仅很有用,而且很直观。
    这意味着,公共语言运行库的某些功能可能在一个环境中比在另一个环境中更突出。您对公共语言运行库的体验取决于所使用的语言编译器或工具。

托管执行过程

   
托管执行过程包括下列步骤:
       1.选择编译器
          为获得公共语言运行库提供的优点,必须使用一个或多个针对运行库的语言编译器。
       2.将代码编译为 Microsoft 中间语言 (MSIL)
          编译将源代码翻译为 MSIL 并生成所需的元数据。
       3.将 MSIL 编译为本机代码
          在执行时,实时 (JIT) 编译器将 MSIL 翻译为本机代码。在此编译过程中,代码必须通过验证过程,该过程检查 MSIL 和元数据以查看是否可以将代码确定为类型安全。
       4.运行代码
          公共语言运行库提供使执行能够发生以及可在执行期间使用的各种服务的结构。

自动内存管理

           
            自动内存管理是公共语言运行库在托管执行过程过程中提供的服务之一。公共语言运行库的垃圾回收器为应用程序管理内存的分配和释放。对开发人员而言,这就意味着在开发托管应用程序时不必编写执行内存管理任务的代码。
        分配内存
            初始化新进程时,运行时会为进程保留一个连续的地址空间区域。这个保留的地址空间被称为托管堆。托管堆维护着一个指针,用它指向将在堆中分配的下一个对象的地址。最初,该指针设置为指向托管堆的基址。托管堆上部署了所有引用类型。应用程序创建第一个引用类型时,将为托管堆的基址中的类型分配内存。应用程序创建下一个对象时,垃圾回收器在紧接第一个对象后面的地址空间内为它分配内存。只要地址空间可用,垃圾回收器就会继续以这种方式为新对象分配空间。
            从托管堆中分配内存要比非托管内存分配速度快。由于运行时通过为指针添加值来为对象分配内存,所以这几乎和从堆栈中分配内存一样快。另外,由于连续分配的新对象在托管堆中是连续存储,所以应用程序可以快速访问这些对象。
        释放内存
            垃圾回收器的优化引擎根据所执行的分配决定执行回收的最佳时间。垃圾回收器在执行回收时,会释放应用程序不再使用的对象的内存。它通过检查应用程序的根来确定不再使用的对象。每个应用程序都有一组根。每个根或者引用托管堆中的对象,或者设置为空。应用程序的根包含全局对象指针、静态对象指针、线程堆栈中的局部变量和引用对象参数以及 CPU 寄存器。垃圾回收器可以访问由实时 (JIT) 编译器和运行时维护的活动根的列表。垃圾回收器对照此列表检查应用程序的根,并在此过程中创建一个图表,在其中包含所有可从这些根中访问的对象。
            不在该图表中的对象将无法从应用程序的根中访问。垃圾回收器会考虑无法访问的对象垃圾,并释放为它们分配的内存。在回收中,垃圾回收器检查托管堆,查找无法访问对象所占据的地址空间块。发现无法访问的对象时,它就使用内存复制功能来压缩内存中可以访问的对象,释放分配给不可访问对象的地址空间块。在压缩了可访问对象的内存后,垃圾回收器就会做出必要的指针更正,以便应用程序的根指向新地址中的对象。它还将托管堆指针定位至最后一个可访问对象之后。请注意,只有在回收发现大量的无法访问的对象时,才会压缩内存。如果托管堆中的所有对象均未被回收,则不需要压缩内存。
            为了改进性能,运行时为单独堆中的大型对象分配内存。垃圾回收器会自动释放大型对象的内存。但是,为了避免移动内存中的大型对象,不会压缩此内存。
        为非托管资源释放内存
            对于应用程序创建的大多数对象,可以依赖垃圾回收器自动执行必要的内存管理任务。但是,非托管资源需要显式清除。最常用的非托管资源类型是包装操作系统资源的对象,例如,文件句柄、窗口句柄或网络连接。虽然垃圾回收器可以跟踪封装非托管资源的托管对象的生存期,但却无法具体了解如何清理资源。创建封装非托管资源的对象时,建议在公共 Dispose 方法中提供必要的代码以清理非托管资源。通过提供 Dispose 方法,对象的用户可以在使用完对象后显式释放其内存。使用封装非托管资源的对象时,应该了解 Dispose 并在必要时调用它。

公共语言运行库中的程序集

 
       程序集是 .NET Framework 应用程序的构造块;程序集构成了部署、版本控制、重复使用、激活范围控制和安全权限的基本单元。程序集是为协同工作而生成的类型和资源的集合,这些类型和资源构成了一个逻辑功能单元。程序集向公共语言运行库提供了解类型实现所需要的信息。
    程序集是 .NET Framework 编程的基本组成部分。程序集执行以下功能:
        包含公共语言运行库执行的代码。如果可移植可执行 (PE) 文件没有相关联的程序集清单,则将不执行该文件中的 Microsoft 中间语言 (MSIL) 代码。请注意,每个程序集只能有一个入口点(即 DllMain、WinMain 或 Main)。
        程序集形成安全边界。程序集就是在其中请求和授予权限的单元。
        程序集形成类型边界。每一类型的标识均包括该类型所驻留的程序集的名称。在一个程序集范围内加载的 MyType 类型不同于在其他程序集范围内加载的 MyType 类型。
        程序集形成引用范围边界。程序集的清单包含用于解析类型和满足资源请求的程序集元数据。它指定在该程序集之外公开的类型和资源。该清单还枚举它所依赖的其他程序集。
        程序集形成版本边界。程序集是公共语言运行库中最小的可版本化单元,同一程序集中的所有类型和资源均会被版本化为一个单元。程序集的清单描述您为任何依赖项程序集所指定的版本依赖性。
        程序集形成部署单元。当一个应用程序启动时,只有该应用程序最初调用的程序集必须存在。其他程序集(例如本地化资源和包含实用工具类的程序集)可以按需检索。这就使应用程序在第一次下载时保持精简。
        程序集是支持并行执行的单元。
        程序集可以是静态的或动态的。静态程序集可以包括 .NET Framework 类型(接口和类),以及该程序集的资源(位图、JPEG 文件、资源文件等)。静态程序集存储在磁盘上的可移植可执行 (PE) 文件中。您还可以使用 .NET Framework 来创建动态程序集,动态程序集直接从内存运行并且在执行前不存储到磁盘上。您可以在执行动态程序集后将它们保存在磁盘上。

元数据
        在过去,以一种语言编写的软件组件(.exe 或 .dll)不能方便地使用以另一种语言编写的软件组件。在这个问题的解决上,COM 向前迈进了一步。.NET Framework 允许编译器向所有的模块和程序集发出附加的说明性信息,从而使组件互用更加简单。这种叫做“元数据”的信息有助于组件无缝交互。
        元数据是一种二进制信息,用以对存储在公共语言运行库可移植可执行文件 (PE) 文件或存储在内存中的程序进行描述。将您的代码编译为 PE 文件时,便会将元数据插入到该文件的一部分中,而将代码转换为 Microsoft 中间语言 (MSIL) 并将其插入到该文件的另一部分中。在模块或程序集中定义和引用的每个类型和成员都将在元数据中进行说明。当执行代码时,运行库将元数据加载到内存中,并引用它来发现有关代码的类、成员、继承等信息。
        元数据以非特定语言的方式描述在代码中定义的每一类型和成员。元数据存储以下信息:
            程序集的说明
                标识(名称、版本、区域性、公钥)。
                导出的类型。
                该程序集所依赖的其他程序集。
                运行所需的安全权限。
            类型的说明
                名称、可见性、基类和实现的接口。
                成员(方法、字段、属性、事件、嵌套的类型)。
            属性
                修饰类型和成员的其他说明性元素。
    元数据的优点
        对于一种更简单的编程模型来说,元数据是关键,该模型不再需要接口定义语言 (IDL) 文件、头文件或任何外部组件引用方法。元数据允许 .NET 语言自动以非特定语言的方式对其自身进行描述,而这是开发人员和用户都无法看见的。另外,通过使用属性,可以对元数据进行扩展。元数据具有以下主要优点:
        自描述文件
            公共语言运行库模块和程序集是自描述的。模块的元数据包含与另一个模块进行交互所需的全部信息。元数据自动提供 COM 中 IDL 的功能,允许将一个文件同时用于定义和实现。运行库模块和程序集甚至不需要向操作系统注册。结果,运行库使用的说明始终反映编译文件中的实际代码,从而提高应用程序的可靠性。
        语言互用性和更简单的基于组件的设计
            元数据提供所有必需的有关已编译代码的信息,以供您从用不同语言编写的 PE 文件中继承类。您可以创建用任何托管语言(任何面向公共语言运行库的语言)编写的任何类的实例,而不用担心显式封送处理或使用自定义的互用代码。
        属性
            .NET Framework 允许您在编译文件中声明特定种类的元数据(称为属性)。在整个 .NET Framework 中到处都可以发现属性的存在,属性用于更精确地控制运行时您的程序如何工作。另外,您可以通过用户定义的自定义属性向 .NET Framework 文件发出您自己的自定义元数据
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值