C# 命名空间是否编译为 IL 文件以“完整”名称

本文关键字:完整 名称 文件 IL 命名空间 是否 编译 | 更新日期: 2023-09-27 17:55:28

例如,如果我有

namespace a
    namespace b
{
    class C...
    class D...
}

那么编译后,在IL文件中,命名空间信息在哪里?我是否得到两个名为 a.b.C 和 a.b.D 的类,其中类名以命名空间名称为前缀?

或者我在程序集文件中得到一个命名空间 a.b,里面有类 C/类 D,就像 C# 代码一样?

C# 命名空间是否编译为 IL 文件以“完整”名称

CLR 对命名空间一无所知。访问类型时,CLR需要知道类型的全名和哪个程序集包含类型的定义,以便运行时可以加载正确的程序集,找到类型,并对其进行操作。

这意味着您在namespace b中的class C存储得像b.C .

另外两个回复写了一些东西,所以我必须写相反的:-)

假设Microsoft在两个阵营中都保持了脚步......阅读 ECMA-335:

114 页

虽然某些编程语言引入了命名空间的概念,但 CLI 中唯一的支持 因为这个概念是一种元数据编码技术。类型名称始终由其完整名称指定 相对于在其中定义它们的程序集的名称。

但即使是这个 ECMA 标准也可以自由地使用命名空间概念:

为了防止将来发生名称冲突,System 命名空间中的所有自定义属性都保留用于标准化。

并且IL语言支持.namespace指令,相当于C#的命名空间指令(该指令在ECMA标准中命名,但没有示例。ILASM 正确编译此示例,反编译代码是人们所期望的)...

.namespace A
{
    .namespace B
    {
        .class public auto ansi beforefieldinit C
            extends [mscorlib]System.Object
        {
            // Nested Types
            .class nested public auto ansi beforefieldinit D
                extends [mscorlib]System.Object
            {
                // Methods
                .method public hidebysig specialname rtspecialname 
                    instance void .ctor () cil managed 
                {
                    // Method begins at RVA 0x2050
                    // Code size 7 (0x7)
                    .maxstack 8
                    IL_0000: ldarg.0
                    IL_0001: call instance void [mscorlib]System.Object::.ctor()
                    IL_0006: ret
                } // end of method D::.ctor
            } // end of class D

            // Methods
            .method public hidebysig specialname rtspecialname 
                instance void .ctor () cil managed 
            {
                // Method begins at RVA 0x2050
                // Code size 7 (0x7)
                .maxstack 8
                IL_0000: ldarg.0
                IL_0001: call instance void [mscorlib]System.Object::.ctor()
                IL_0006: ret
            } // end of method C::.ctor
        } // end of class A.B.C
    }
}

但请注意,生成的代码等效于不使用.namespace并直接在.class中包含全名:

.class public auto ansi beforefieldinit A.B.C
    extends [mscorlib]System.Object
{
    // Nested Types
    .class nested public auto ansi beforefieldinit D
        extends [mscorlib]System.Object
    {
        // Methods
        .method public hidebysig specialname rtspecialname 
            instance void .ctor () cil managed 
        {
            // Method begins at RVA 0x2050
            // Code size 7 (0x7)
            .maxstack 8
            IL_0000: ldarg.0
            IL_0001: call instance void [mscorlib]System.Object::.ctor()
            IL_0006: ret
        } // end of method D::.ctor
    } // end of class D

    // Methods
    .method public hidebysig specialname rtspecialname 
        instance void .ctor () cil managed 
    {
        // Method begins at RVA 0x2050
        // Code size 7 (0x7)
        .maxstack 8
        IL_0000: ldarg.0
        IL_0001: call instance void [mscorlib]System.Object::.ctor()
        IL_0006: ret
    } // end of method C::.ctor
} // end of class A.B.C

然后,Type类具有NameNamespace属性。Type类(作为 mscorlib 的一大块)是 CLR 良好工作不可或缺的一部分。

因此,如果问题是:编译的 .NET 程序中是否有显式命名空间?回答是"否"。只有全名。

如果问题是:.NET 中是否存在命名空间的概念(在任何/所有级别)?回答是"是"。它存在于IL(源代码级别,.namespace),CLR(.NET API,Type.Namespace),C#(.NET的"主要"语言,用于编写几乎所有的.NET库)(namespace)。

IL 中不存在命名空间。它们不会保留为元数据。在 IL 中,对类型和方法的所有引用都必须使用完全限定的名称进行。

但是,IL 识别并显式允许类型名称包含点。C# 或 VB.NET 命名空间是在此基础上构建的语言构造:它们本质上是一种机制,允许您仅指定类型名称的后面以点分隔的部分。 using或命名空间Imports指令是编译器猜测不完整类型名称的前一部分的提示;但是在所有情况下,编译器都必须将非完全限定名(SomeType)转换为完全限定的类型名(SomeNamespace.SomeType)。

让我们先直截了当地说点。

  • IL基本上只是.NET中使用的"基本构建块"语言
  • DLL 包含 IL 和元数据

如果您启动ildasm(从 Visual Studio 命令提示符),将 DLL 放在那里,转储所有内容,然后在您喜欢的文本编辑器中浏览它,您可以看到两者。这将包含元数据和 IL 代码。

IL 不包含命名空间信息,它只是由"令牌"组成,这些令牌基本上是类、函数等的 ID。这些 ID 可以解析。

DLL 中的一件事是命名空间的元数据容器。或者正如ILDASM将向您展示的那样:

// ================================= M E T A I N F O ================================
// ===========================================================
// ScopeName : MyNamespace
// MVID      : {22EE923F-126A-43BA-8A72-59A7A069625A}
// ===========================================================
// Global functions
// -------------------------------------------------------
// 
// Global fields
// -------------------------------------------------------
// 
// Global MemberRefs
// -------------------------------------------------------
// 
// TypeDef #1 (02000002)
// -------------------------------------------------------
//  TypDefName: MyNamespace.MyType  (02000002)
//  Flags     : [Public] [AutoLayout] [Class] [AnsiClass]  (00000001)
//  Extends   : 01000001 [TypeRef] System.Object
//  Field #1 (04000001)

现在,您基本上称为命名空间的是以相同的"点"前缀开头的 typedefs 元数据令牌的集合。命名空间并不是真正的"实体",这意味着它本身没有令牌。命名空间之所以存在,是因为存在包含点(具有标记)的类型名称。换句话说,命名空间不直接存在;它派生自那里的类型。

现在,知道了这一点,您的问题可以很容易地回答:

那么编译后,在IL文件中,命名空间信息在哪里?做 我得到两个名为 a.b.C 和 a.b.D 的类,其中类名是 以命名空间名称为前缀?

是的,没有它们,命名空间就不会存在。