在 .NET BCL 中有循环引用:
System.dll
和System.Xml.dll
System.dll
和System.Configuration.dll
System.Xml.dll
和System.Configuration.dll
这是 .NET Reflector 的屏幕截图,显示了我的意思:
Microsoft 如何创建这些程序集对我来说是个谜。是否需要特殊的编译过程才能允许这样做?我想这里正在发生一些有趣的事情。
在 .NET BCL 中有循环引用:
System.dll
和System.Xml.dll
System.dll
和System.Configuration.dll
System.Xml.dll
和System.Configuration.dll
这是 .NET Reflector 的屏幕截图,显示了我的意思:
Microsoft 如何创建这些程序集对我来说是个谜。是否需要特殊的编译过程才能允许这样做?我想这里正在发生一些有趣的事情。
我只能说出 Mono Project 是如何做到这一点的。这个定理很简单,尽管它给代码带来了混乱。
他们首先编译 System.Configuration.dll,不需要引用 System.Xml.dll 的部分。在此之后,他们以正常方式编译 System.Xml.dll。现在魔法来了。他们重新编译 System.configuration.dll,其中需要引用 System.Xml.dll 的部分。现在使用循环引用成功编译。
简而言之:
RBarryYoung 和 Dykam 正在做一些事情。Microsoft 使用内部工具,该工具使用 ILDASM 来反汇编程序集,剥离所有内部/私有内容和方法体,然后再次(使用 ILASM)将 IL 重新编译为所谓的“脱水程序集”或元数据程序集。每次更改程序集的公共接口时都会执行此操作。
在构建期间,使用元数据程序集而不是真实的程序集。这样循环就被打破了。
可以按照 Dykam 描述的方式完成,但 Visual Studio 会阻止您这样做。
您必须直接使用命令行编译器 csc.exe。
csc /目标:库 ClassA.cs
csc /target:library ClassB.cs /reference:ClassA.dll
csc /target:library ClassA.cs ClassC.cs /reference:ClassB.dll
//ClassA.cs
namespace CircularA {
public class ClassA {
}
}
//ClassB.cs
using CircularA;
namespace CircularB {
public class ClassB : ClassA {
}
}
//ClassC.cs
namespace CircularA {
class ClassC : ClassB {
}
}
只要您不使用项目引用,它在 Visual Studio 中很容易做到……试试这个:
所以这就是你的做法。但说真的......你不要在一个真正的项目中这样做!如果你这样做,圣诞老人今年不会给你任何礼物。
我想这可以通过从一组非循环程序集开始并使用 ILMerge 将较小的程序集合并为逻辑相关组来完成。
好吧,我从来没有在 Windows 上做过,但我已经在很多 compile-link-rtl 环境中做过,这些环境是它的实际祖先。你要做的是首先制作没有交叉引用的存根“目标”然后链接,然后添加循环引用,然后重新链接。链接器通常不关心循环引用或跟随引用链,他们只关心能够自行解析每个引用。
因此,如果您有两个库 A 和 B 需要相互引用,请尝试以下操作:
Dykam 提出了一个很好的观点,它是编译的,而不是 .Net 中的链接,但原理保持不变:使用它们导出的入口点制作您的交叉引用源,但除了其中一个之外,所有源都有自己对其他引用的存根出去。像这样建造它们。然后,取消存根外部引用并重建它们。即使没有任何特殊工具,这也应该可以工作,事实上,这种方法适用于我曾经尝试过的每个操作系统(大约 6 个)。虽然很明显,自动化它会是一个很大的帮助。
一种可能的方法是使用条件编译 (#if) 首先编译不依赖于其他程序集的 System.dll,然后编译其他程序集,最后重新编译 System.dll 以包含依赖于 Xml 和配置。
从技术上讲,这些可能根本没有编译,而是手工组装的。毕竟,这些都是低级库。
同意。asmmeta.exe 类似于 ildasm,但省略了所有的 IL(只是 ret)和一些私人信息,尽管有时需要私人信息,例如结构大小。
更普遍的想法是多通道构建,微软一直非常依赖它。
剥离的 ildasm 输出可以被认为是“头”文件,在一个没有真正拥有它们的系统中。
首先访问每个运行 ilasm 的目录(具有大量并行性!)。然后访问运行 csc 的每个目录(同样具有大量并行性)。在 csc 之后,在同一通道中,运行 like-ildasm 工具,输出回原始的“标题”。比较它们。如果有任何不匹配,则构建被破坏。开发人员未能更新标头。只是修补它,而不重新启动构建已经太晚了(也许使用适当的依赖关系图,大多数目录都不会受到影响)。
这也是一种轻松升级版本的方法。like-ilasm 代码可以有版本号的名称。尽管这实际上是多通道构建的次要结果。