使用名称空间时 C# 编译的内容
What C# compiles when using namespaces
我们有两个应用程序,每个应用程序都编译自己的代码,并共享一些代码(主要是数据相关的)。将此设计拆分为三个命名空间并尝试强制执行该命名空间 Foo
永远不会导入命名空间 Bar
但两者都可以导入命名空间 Shared
.
感觉很自然
我希望维恩图作为一种可视化方式受到赞赏:
但是 Foo
和 Bar
之间的 class 中的一个从裂缝中溜走了,有人在 Foo
中引用了 Bar
中的 class =] 尽管强制执行。
这让我想知道 C# 编译器实际上是如何处理这个问题的?在我看来,可能会发生以下两种情况之一。
- 整个命名空间被编译成
Foo
。让图表看起来像这样:
- 或者编译器足够聪明,可以提取必需的 class。使图表看起来像这样:
我似乎找不到任何关于 usings 和命名空间如何编译的文档。命名空间似乎只是为开发人员而不是编译器组织代码。然而他们提供了范围......所以我想#2适用吗?如何测试这个?
命名空间和程序集之间没有对应关系:一个程序集可能包含多个命名空间,一个命名空间可能跨越多个程序集。
程序集中编译的 IL 代码通过完全限定名称引用类型:Foo.SomeClass
而不是 SomeClass
,Bar.OtherClass
而不是 OtherClass
,等等.编译器的工作是在编写缩写形式 SomeClass
时找出 哪个 完全限定的类型名称 - 因为您可以定义一个 class 调用SomeClass
在命名空间 Foo
、Bar
甚至 System
!
中
当你写:
namespace Foo
{
public class SomeClass
{
}
}
您正在定义一个具有完全限定名称的类型 Foo.SomeClass
。
当你写:
using Foo;
...
SomeClass instance = new SomeClass();
编译器将其视为:
Foo.SomeClass instance = new Foo.SomeClass();
命名空间只是为了方便组织这些完全限定名称而构造的。当您说 using Foo;
时,您只是告诉编译器在您键入 SomeClass
时搜索以 Foo.
开头的完全限定名称。当你写 using Foo;
时,没有什么是 "imported",它只是提供了一个方便的替代方法,而不是到处写 Foo.SomeClass
;您的 using
或 namespace
也不会生成任何 "code"(在发出 IL 指令的意义上)。它所做的只是告诉编译器在您编写 SomeClass
.
时将 Foo.SomeClass
放入 IL
以上是规范中定义的一组更细微的规则的简化,用于解析短格式类型名称;您可以阅读此内容了解更多详情:here and here
您希望在图表中强制执行依赖关系的级别是程序集引用级别:如果 Foo 项目从不引用 Bar 程序集,反之亦然,即使您尝试,代码也不会编译从一个程序集中引用另一个程序集中的类型。命名空间与此根本没有太大关系,因为同样,没有什么能阻止您在 Foo
命名空间中定义类型,而是在 Bar
程序集中定义类型。
我们有两个应用程序,每个应用程序都编译自己的代码,并共享一些代码(主要是数据相关的)。将此设计拆分为三个命名空间并尝试强制执行该命名空间 Foo
永远不会导入命名空间 Bar
但两者都可以导入命名空间 Shared
.
我希望维恩图作为一种可视化方式受到赞赏:
但是 Foo
和 Bar
之间的 class 中的一个从裂缝中溜走了,有人在 Foo
中引用了 Bar
中的 class =] 尽管强制执行。
这让我想知道 C# 编译器实际上是如何处理这个问题的?在我看来,可能会发生以下两种情况之一。
- 整个命名空间被编译成
Foo
。让图表看起来像这样: - 或者编译器足够聪明,可以提取必需的 class。使图表看起来像这样:
我似乎找不到任何关于 usings 和命名空间如何编译的文档。命名空间似乎只是为开发人员而不是编译器组织代码。然而他们提供了范围......所以我想#2适用吗?如何测试这个?
命名空间和程序集之间没有对应关系:一个程序集可能包含多个命名空间,一个命名空间可能跨越多个程序集。
程序集中编译的 IL 代码通过完全限定名称引用类型:Foo.SomeClass
而不是 SomeClass
,Bar.OtherClass
而不是 OtherClass
,等等.编译器的工作是在编写缩写形式 SomeClass
时找出 哪个 完全限定的类型名称 - 因为您可以定义一个 class 调用SomeClass
在命名空间 Foo
、Bar
甚至 System
!
当你写:
namespace Foo
{
public class SomeClass
{
}
}
您正在定义一个具有完全限定名称的类型 Foo.SomeClass
。
当你写:
using Foo;
...
SomeClass instance = new SomeClass();
编译器将其视为:
Foo.SomeClass instance = new Foo.SomeClass();
命名空间只是为了方便组织这些完全限定名称而构造的。当您说 using Foo;
时,您只是告诉编译器在您键入 SomeClass
时搜索以 Foo.
开头的完全限定名称。当你写 using Foo;
时,没有什么是 "imported",它只是提供了一个方便的替代方法,而不是到处写 Foo.SomeClass
;您的 using
或 namespace
也不会生成任何 "code"(在发出 IL 指令的意义上)。它所做的只是告诉编译器在您编写 SomeClass
.
Foo.SomeClass
放入 IL
以上是规范中定义的一组更细微的规则的简化,用于解析短格式类型名称;您可以阅读此内容了解更多详情:here and here
您希望在图表中强制执行依赖关系的级别是程序集引用级别:如果 Foo 项目从不引用 Bar 程序集,反之亦然,即使您尝试,代码也不会编译从一个程序集中引用另一个程序集中的类型。命名空间与此根本没有太大关系,因为同样,没有什么能阻止您在 Foo
命名空间中定义类型,而是在 Bar
程序集中定义类型。