.NET 中的托管代码和非托管代码

2025 年 6 月 28 日 | 阅读 9 分钟

在 .NET 中,托管代码指的是使用 .NET 框架支持的语言(如 C#、Visual Basic .NET (VB.NET)、F# 等)编写的程序代码。这种代码旨在运行在通用语言运行时 (CLR) 中,CLR 是 .NET 框架负责执行 .NET 程序的关键组件。

托管代码的定义包含以下特性:

Managed code and Unmanaged code in .NET
  1. CLR 执行: 托管代码在 CLR 的控制和管理下执行。
  2. 平台无关性: 运行时,托管代码是平台无关的。CLR 抽象了硬件和操作系统的差异,使得相同的代码可以在任何安装了相应 CLR 实现的系统上运行。

通用语言运行时 (CLR)

CLR 是 .NET 框架的重要组成部分,负责协调 .NET 程序的执行。当您使用 C#、VB.NET、F# 等 .NET 支持的语言编写代码时,源代码会被编译成一种中间语言,称为通用中间语言 (CIL) 或 MSIL (Microsoft Intermediate Language)。

托管代码中的中间语言

中间语言 (IL),也称为通用中间语言 (CIL) 或 Microsoft 中间语言 (MSIL),是 .NET 框架中托管代码的一个基本组成部分。当您编译使用 .NET 支持的语言(如 C#、VB.NET 或 F#)编写的源代码时,它会被转换为 IL,而不是直接转换为机器代码。

中间语言 (IL) 的作用

i) 平台无关性: IL 是一种平台无关的中间代码。它可以部署到任何处理器或操作系统上,允许相同的 IL 代码在安装了适当通用语言运行时 (CLR) 实现的任何系统上执行。

ii) CLR 执行: IL 代码由 CLR 执行,CLR 执行即时 (JIT) 编译。在运行时,CLR 将 IL 代码转换为适用于底层硬件的原生机器代码,从而实现

iii) 在 JIT 编译之前, CLR 会对 IL 代码进行类型检查和安全性验证,确保其正确性并符合类型安全标准。这降低了常见编程错误的发生几率,并提高了安全性。

iv) 优化和执行: 在执行过程中,CLR 的 JIT 编译器会针对当前平台优化 IL 代码,根据硬件和运行时环境的详细信息进行调整以提高性能。

v) 互操作性: IL 使得不同语言之间的通信更加容易。无论代码是用什么语言编写的,一旦编译成 IL,就可以与其他用不同语言编写的组件或库顺畅地进行通信

托管代码的执行

  1. 编译: 当您使用 .NET 语言编写代码时,它会被编译成 CIL/MSIL,而不是机器代码。这种中间语言是平台无关的,CPU 无法直接运行。
  2. 即时 (JIT) 编译: 当您运行 .NET 程序时,CLR 会介入。CLR 提供一个即时 (JIT) 编译器,该编译器将中间语言 (CIL/MSIL) 代码翻译成本地机器代码,以便底层 CPU 可以理解。
  3. 内存管理: CLR 通过垃圾回收器管理内存的分配和释放。当对象不再使用或被引用时,垃圾回收器会检测并释放这些对象所占用的内存。这种自动内存管理有助于防止内存泄漏,并简化了开发人员的内存管理工作。
  4. .NET 中的托管代码是类型安全的, 这可以防止诸如访问数组范围之外的内存、空引用异常等常见问题以及其他与内存相关的困难。在 JIT 编译之前,CLR 会对代码进行类型检查和安全检查,有助于防止许多常见的编程问题。
  5. 异常处理: CLR 包含一个结构化的异常处理系统,允许开发人员灵活地处理异常。它确保异常不会中断应用程序的流程,并且可以根据需要捕获、处理并向上层调用堆栈传播。

托管代码的优势

i) 可移植性: 托管代码能够在安装了相应 CLR 实现的任何平台上执行,从而促进了平台无关性。

ii) 自动内存管理: CLR 的垃圾回收器负责管理内存,降低了内存泄漏的可能性,并简化了开发人员与内存相关的任务。

iii) 类型安全: 有助于防止许多常见的编程错误和漏洞。

iv) 安全性: 托管应用程序内置的安全功能提高了其安全性和可信度。

托管 .NET 在何处以及如何使用?

由于通用语言运行时 (CLR) 提供的优势和功能,.NET 中的托管代码被广泛应用于各种应用程序和软件开发环境。

以下是托管代码广泛使用的一些示例:

i) 桌面应用程序: 许多桌面应用程序是用 .NET 托管代码编写的。例如,使用 Windows Presentation Foundation (WPF) 或 Windows Forms (WinForms) 创建的应用程序是用 C#、VB.NET 或其他 .NET 语言编写的。

ii) Web 应用程序: 流行的 Web 框架 ASP.NET 允许开发人员使用托管代码创建 Web 应用程序。C# 或 VB.NET 用于开发 ASP.NET 应用程序,如 ASP.NET Web Forms 和 ASP.NET MVC。

iii) 移动应用程序: Xamarin 是一个跨平台开发框架,允许您使用托管代码创建 iOS 和 Android 移动应用程序。

iv) 企业应用程序: 在开发企业级应用程序(如客户关系管理 (CRM)、企业资源规划 (ERP) 和其他业务软件解决方案)时,经常使用。

v) 基于云的服务: .NET 促进了使用 Microsoft Azure 等平台的基于云的服务和应用程序。托管代码可用于创建 Azure Functions、Azure Web Apps 等服务。

.NET 中非托管代码的背景

指在通用语言运行时 (CLR) 的控制和管理之外运行的代码。CLR 是 .NET 框架中负责运行托管代码的运行时环境。非托管代码通常由使用 C 或 C++ 等语言开发的程序或组件组成,这些程序或组件不依赖于 CLR 的服务。

非托管代码在 .NET 中具有以下主要属性和方面:

1. 内存管理

- 手动内存分配:非托管代码需要动态内存管理,而托管代码则受益于 CLR 垃圾回收器提供的自动内存管理。开发人员需要在 C 或 C++ 等语言中使用 malloc() 和 free() 等函数手动分配和释放内存。

- 潜在的内存泄漏:由于非托管代码依赖于手动内存管理,如果内存分配或释放不正确,它更容易发生内存泄漏和指针相关问题。

2. 平台依赖性

- 直接与硬件和操作系统交互:由于非托管代码更直接地与底层硬件和操作系统交互,因此它可能变得平台依赖。未经修改或重新编译,为一种平台设计的代码可能无法在另一种平台上运行。

3. 性能考虑

- 性能优化:非托管代码为开发人员提供了对内存和资源的更多控制,允许他们优化代码以提高性能。这对于需要极高效率或对硬件资源进行低级访问的任务尤其重要。

4. CLR 服务的缺失

- 无托管代码服务:非托管代码无法受益于 CLR 的服务,如自动内存管理、类型安全强制执行和异常处理。开发人员负责手动处理这些事情。

5. 互操作性

- 与现有系统集成:非托管代码通常用于连接现有的原生库或用 C 或 C++ 等语言开发的系统。.NET 提供了诸如平台调用服务 (P/Invoke) 和 COM Interop 等技术,以允许托管代码与非托管代码交互。

以下是一些非托管代码可能在 .NET 生态系统中使用的场景:

i) 旧系统集成: 连接到现有的 C/C++ 库或系统。

ii) 性能关键操作: 使用优化算法或直接访问系统级资源。

iii) 当应用程序需要平台特定功能或优化时,使用平台特定开发。

在处理需要低级资源控制、性能优化或与不允许托管代码执行的系统交互的场景时,理解 .NET 中的非托管代码至关重要。另一方面,非托管代码通常会给开发人员带来更大的复杂性和责任,涉及内存管理和平台要求。

优点

1. 性能优化

i) 直接内存访问: 由于非托管代码允许直接访问,开发人员可以微调内存使用并优化关键流程的性能。与托管程序相比,这种直接访问可能导致更有效的内存消耗。

ii) 非托管代码为开发人员提供了对硬件资源的低级访问,使他们能够整合特定于设备架构的优化,从而大大提高性能。

2. 与现有系统互操作

i) 与遗留代码兼容: 非托管代码更容易与用 C 或 C++ 等不支持托管代码执行的语言编写的现有原生库或系统集成,从而使应用程序能够轻松地与遗留系统通信。

ii) 互操作性 提供诸如平台调用服务 (P/Invoke) 或 COM Interop 等协议,这些协议允许托管代码与非托管代码连接并使用原生库或系统提供的功能。

3. 平台特定功能

i) 平台特定操作: 非托管代码允许执行平台特定操作或需要对操作系统功能进行低级访问。当处理平台特定功能时,此功能至关重要。

4. 直接硬件访问

i) 低级系统操作: 非托管代码可以直接连接到硬件资源,从而能够执行需要低级系统操作或硬件特定优化(在托管代码中难以实现)的任务。

5. 性能关键活动

i) 高度优化的算法: 对于需要最高性能和效率的活动,非托管代码允许开发人员设计高度优化的算法和数据结构,这些算法和数据结构可以利用 C 或 C++ 等语言提供的低级控制。

6. 降低开销

i) 低开销:由于非托管代码比托管代码更接近框架级别运行,因此其运行时开销较小,当关键在于最小化开销时,这可能是有益的。

限制

i) 手动内存管理:非托管代码需要显式的内存分配和释放,如果管理不当,可能会导致内存泄漏、悬挂指针和其他内存相关问题。

ii) 平台依赖性:由于非托管代码通常直接与底层硬件和操作系统交互,因此它可能是平台依赖的。未经修改或重新编译,为一种平台设计的代码可能无法在另一种平台上顺利运行。

iii) 由于缺乏 CLR 管理的安全措施,非托管代码可能更容易受到安全漏洞、缓冲区溢出和其他与内存相关的安全问题的影响。

iv) 非托管代码无法访问自动内存管理、类型安全强制执行、结构化异常处理和运行时优化等 CLR 功能。开发人员负责一切。

.NET 中托管代码与非托管代码的区别

方面托管代码非托管代码
内存管理- CLR(垃圾回收器)自动管理内存。- 使用 malloc() 和 free() 例程手动管理内存。
执行环境- 通用语言运行时 (CLR) 控制程序的运行方式。- 在 CLR 之外独立运行。
平台无关性- 由于 CLR 的抽象,运行时是平台无关的。- 与硬件和操作系统的交互增加,使其具有平台依赖性。
类型安全CLR 强制执行此项以防止常见编程错误。- 需要手动处理,这可能导致内存问题。
互操作性- 它与其他语言和组件无缝交互。- 通过互操作性方法,可以与非 .NET 库进行接口。
性能- 由于托管环境,存在一定的运行时开销。- 直接内存访问和低级控制可能潜在地提高性能优化。
安全性- 基于 CLR 的安全技术提供安全优势。- 没有 CLR 管理的安全功能。
硬件- 直接访问 CLR 提供有限的访问和抽象。- 可以直接与硬件资源交互。

结论

在 .NET 框架中,托管代码指的是在 CLR 受控环境中运行的应用程序,受益于自动内存管理、平台抽象、类型安全和 CLR 提供的服务。这种代码用 C#、VB.NET 或 F# 等语言编写,在 CLR 的监督下执行,实现了平台无关的执行、安全功能以及各种语言之间的轻松兼容性。另一方面,非托管代码在 CLR 控制之外运行,通常用 C 或 C++ 等语言编写,需要显式内存管理,并提供直接硬件访问。它用于与遗留系统交互、实现性能关键操作以及实现平台特定功能等任务,利用其在低级优化和直接硬件交互方面的潜力,但缺乏托管代码架构固有的自动内存管理和基于 CLR 的服务的优势。