109

在 .NET BCL 中有循环引用:

  • System.dllSystem.Xml.dll
  • System.dllSystem.Configuration.dll
  • System.Xml.dllSystem.Configuration.dll

这是 .NET Reflector 的屏幕截图,显示了我的意思:

在此处输入图像描述

Microsoft 如何创建这些程序集对我来说是个谜。是否需要特殊的编译过程才能允许这样做?我想这里正在发生一些有趣的事情。

4

9 回答 9

58

我只能说出 Mono Project 是如何做到这一点的。这个定理很简单,尽管它给代码带来了混乱。

他们首先编译 System.Configuration.dll,不需要引用 System.Xml.dll 的部分。在此之后,他们以正常方式编译 System.Xml.dll。现在魔法来了。他们重新编译 System.configuration.dll,其中需要引用 System.Xml.dll 的部分。现在使用循环引用成功编译。

简而言之:

  • A 编译时不需要 B 的代码和对 B 的引用。
  • B 已编译。
  • A 被重新编译。
于 2009-08-22T17:43:49.410 回答
35

RBarryYoung 和 Dykam 正在做一些事情。Microsoft 使用内部工具,该工具使用 ILDASM 来反汇编程序集,剥离所有内部/私有内容和方法体,然后再次(使用 ILASM)将 IL 重新编译为所谓的“脱水程序集”或元数据程序集。每次更改程序集的公共接口时都会执行此操作。

在构建期间,使用元数据程序集而不是真实的程序集。这样循环就被打破了。

于 2009-08-22T17:58:05.887 回答
27

可以按照 Dykam 描述的方式完成,但 Visual Studio 会阻止您这样做。

您必须直接使用命令行编译器 csc.exe。

  1. csc /目标:库 ClassA.cs

  2. csc /target:library ClassB.cs /reference:ClassA.dll

  3. 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 {
    }
}
于 2009-08-22T17:50:47.307 回答
18

只要您不使用项目引用,它在 Visual Studio 中很容易做到……试试这个:

  1. 打开视觉工作室
  2. 创建 2 个类库项目“ClassLibrary1”和“ClassLibrary2”。
  3. 建造
  4. 通过浏览到在步骤 3 中创建的 dll,从 ClassLibrary1 添加对 ClassLibrary2 的引用。
  5. 通过浏览到在步骤 3 中创建的 dll,从 ClassLibrary2 添加对 ClassLibrary1 的引用。
  6. 再次构建(注意:如果您在两个项目中都进行了更改,则需要构建两次以使两个引用都“新鲜”)

所以这就是你的做法。但说真的......你不要在一个真正的项目中这样做!如果你这样做,圣诞老人今年不会给你任何礼物。

于 2009-08-24T03:07:11.030 回答
6

我想这可以通过从一组非循环程序集开始并使用 ILMerge 将较小的程序集合并为逻辑相关组来完成。

于 2009-08-22T17:29:44.820 回答
4

好吧,我从来没有在 Windows 上做过,但我已经在很多 compile-link-rtl 环境中做过,这些环境是它的实际祖先。你要做的是首先制作没有交叉引用的存根“目标”然后链接,然后添加循环引用,然后重新链接。链接器通常不关心循环引用或跟随引用链,他们只关心能够自行解析每个引用。

因此,如果您有两个库 A 和 B 需要相互引用,请尝试以下操作:

  1. 在没有任何参考的情况下将 A 链接到 B。
  2. 将B参考链接到 A。
  3. 链接 A,将参考添加到 B。

Dykam 提出了一个很好的观点,它是编译的,而不是 .Net 中的链接,但原理保持不变:使用它们导出的入口点制作您的交叉引用源,但除了其中一个之外,所有源都有自己对其他引用的存根出去。像这样建造它们。然后,取消存根外部引用并重建它们。即使没有任何特殊工具,这也应该可以工作,事实上,这种方法适用于我曾经尝试过的每个操作系统(大约 6 个)。虽然很明显,自动化它会是一个很大的帮助。

于 2009-08-22T17:44:33.650 回答
1

一种可能的方法是使用条件编译 (#if) 首先编译不依赖于其他程序集的 System.dll,然后编译其他程序集,最后重新编译 System.dll 以包含依赖于 Xml 和配置。

于 2009-08-22T17:42:06.590 回答
0

从技术上讲,这些可能根本没有编译,而是手工组装的。毕竟,这些都是低级库。

于 2009-10-06T17:02:47.193 回答
0

同意。asmmeta.exe 类似于 ildasm,但省略了所有的 IL(只是 ret)和一些私人信息,尽管有时需要私人信息,例如结构大小。

更普遍的想法是多通道构建,微软一直非常依赖它。

剥离的 ildasm 输出可以被认为是“头”文件,在一个没有真正拥有它们的系统中。

首先访问每个运行 ilasm 的目录(具有大量并行性!)。然后访问运行 csc 的每个目录(同样具有大量并行性)。在 csc 之后,在同一通道中,运行 like-ildasm 工具,输出回原始的“标题”。比较它们。如果有任何不匹配,则构建被破坏。开发人员未能更新标头。只是修补它,而不重新启动构建已经太晚了(也许使用适当的依赖关系图,大多数目录都不会受到影响)。

这也是一种轻松升级版本的方法。like-ilasm 代码可以有版本号的名称。尽管这实际上是多通道构建的次要结果。

于 2021-11-04T01:22:20.027 回答