.NET 托管、非托管、本地:这些代码有什么区别?

http://www.codeguru.com/Csharp/.NET/cpp_managed/article.php/c4871

本文内容

  • 什么是托管代码?
  • 什么是非托管代码?
  • 什么是本地代码?
  • 托管代码意味着托管数据?

虽然该文章写的时间比较早,但是完全可以说明问题。

2003 年 4 月 24 日 VS 2003(之前称为 Everett)发布,很多开发者愿意考虑使用托管代码的新技术。但是对这个新技术有点迷茫,特别是 C++ 开发者。因为,正如我之前指出的,C++ 是特殊的。

什么是托管代码(Managed Code)?


托管代码是 Visual Basic .NET 和 C# 编译器创建。它们编译成中间语言(IL),而不是直接在计算机上运行的机器代码。IL 被保存在一个称为程序集中,以及描述代码的类、方法、和属性(如安全性要求)的元数据文件。在 .NET 里,程序集是“一站式(one-stop-shopping)”部署的单元。可以将程序集直接复制到另一台服务器上进行部署。

托管代码运行在公共语言运行库(Common Language Runtime,CLR)。CLR 为正在运行的代码提供多种服务。在通常代码事件中,它首先加载和验证程序集,确保 IL 正确。然后,当程序集运行,方法被调用时,CLR 安排它们被编译为适合机器的机器码,并缓存该机器码,以便下次被调用时使用。这被称为 Just In Time,或 JIT compiling,或简称 Jitting。

当程序集运行时,CLR 继续提供诸如安全、内存管理、线程和其他的服务。应用程序是由 CLR 来管理的。

Visual Basic .NET 和 C# 只能产生托管代码。如果你用的是这些应用程序,那么你就正在使用托管代码。Visual C ++ .NET 也能产生托管代码,就是当你创建一个项目,选择名为“.Managed”开始的应用程序类型,如“.Managed C++ application”。

什么是非托管代码(Unmanaged Code)?


非托管代码是你以前用 Visual Studio .NET 2002 创建的代码。现在你还有很多 Visual Basic 6、Visual C ++ 6,甚至是使用了 15 年的老 C 编译器产生的非托管代码。非托管代码直接被编译成机器码,跟你具有相同或几乎相同的芯片的机器上都能运行。但是,它不能从不可见的运行时里获得诸如安全或内存管理等服务,而只能从操作系统获得,通过调用 Windows SDK 提供的 API。最近,有越来越多的非托管应用程序通过 COM 调用获得操作系统的服务。

不同于 Visual Studio 2003 中的其他语言,Visual C ++ 可以创建非托管应用程序。当你创建一个项目,选择以 MFC、ATL 或 Win32 开头的应用程序,就创建了一个非托管的应用程序。

这导致一些混淆,也就是说,当创建一个托管的 C++ 应用程序时,那么生成结果是一个 .exe 扩展名的 IL 装配文件。当你创建一个 MFC 应用程序,生成结果是一个本地代码(native code)的 Windows 可执行文件,扩展名也是 .exe。这两个文件的内部布局完全不同。你可以使用中间语言反汇编工具(Intermediate Language Disassembler)ildasm,查看内部装配,以及元数据文件和 IL。在非托管的 .exe 文件使用 ildasm,你会被告知没有有效的 CLR 头,不能反汇编——虽然扩展名相同,但文件完全不同。

什么是本地代码(Native Code)?


本地代码在两种情况下使用。许多人把非托管代码作为本地代码的同义词:用旧的编译工具或是 Visual C ++ 生成代码,不会运行在 CLR 上,而是在计算机本地。这可能是一个完整的应用,也可能是一个 COM 组件或通过 COM Interop 或 PInvoke 由托管代码调用的 DLL。COM Interop 和 PInvoke 是强大的工具,确保,当你将旧代码移动到新环境时,仍然可以使用。我更喜欢说非托管代码。从这个意义上,因为它强调的是代码不会得到运行时的服务。例如,托管代码中代码访问安全性(Code Access Security),会阻止代码从另一个服务装载代码,防止破坏性的行为。如果你的应用程序从另一个服务加载非托管代码,就无法得到这种保护。

本地代码的另一个使用是描述了 JIT 编译器的输出,机器代码事实上是运行在运行时上。这是托管的,不是 IL,而是机器码。因此,不能假设本地代码等同于非托管代码。

托管代码意味着托管数据?


对于 Visual Basic 和 C#,生存期问题很简单,因为你别无选择。当你声明一个类,在托管堆上创建该类的实例,而垃圾回收器关心该实例的生存期问题。但是在 Visual C++,你是有选择的。即使你创建一个托管应用程序,也可以由你决定类是否是一个托管类型或一个非托管类型。

下面是一个非托管类型:

class Foo
{
    private:
        int x;
    public:
        Foo(): x(0){}
        Foo(int xx): x(xx) {}
};

下面是一个托管类型:

__gc class Bar
{
    private:
        int x;
    public:
        Bar(): x(0){}
        Bar(int xx): x(xx) {}
};

唯一的区别是Bar定义前边的 __gc 关键字,但却有很大不同。关于 __gc 关键字参见MSDN:ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_fxintro/html/6a1f9b6c-9983-4722-8b58-13e9af2a1339.htm

托管类型是可以被回收的。它们必须用new来创建,不在堆上。因此,下面是对的:

Foo f;

但下面是不允许的:

Bar b;

如果我必须在堆上创建Foo的一个实例,那一定要记着清除它:

Foo* pf = new Foo(2);
// . . .
delete pf;

在C ++编译器实际上使用两个堆,一个是托管的,一个是非托管的,使用重载的new操作符,当创建用一个new创建实例时,来确定分配的内存在哪里。如果在堆上创建Bar的一个实例,那么可以忽略它。垃圾回收器将会回收它。在 Visual C++,你是有选择的。

你可能感兴趣的:(.net)