Microsoft 如何创建具有循环引用的程序集?

发布于 2024-12-02 07:52:36 字数 445 浏览 4 评论 0原文

System.dllSystem.dll之间存在循环引用

  • 在 .NET BCL 中, System.dllSystem.Xml.dll
  • 。 Configuration.dll
  • System.Xml.dllSystem.Configuration.dll

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

在此处输入图像描述

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

In the .NET BCL there are circular references between:

  • System.dll and System.Xml.dll
  • System.dll and System.Configuration.dll
  • System.Xml.dll and System.Configuration.dll

Here's a screenshot from .NET Reflector that shows what I mean:

enter image description here

How Microsoft created these assemblies is a mystery to me. Is a special compilation process required to allow this? I imagine something interesting is going on here.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

宁愿没拥抱 2024-12-09 07:52:36

我只能告诉 Mono 项目是如何做到这一点的。该定理非常简单,尽管它使代码混乱。

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

简而言之:

  • A 是在没有代码的情况下编译的
    需要 B 和对 B 的引用。
  • B 已编译。
  • A 重新编译。

I can only tell how the Mono Project does this. The theorem is quite simple, though it gives a code mess.

They first compile System.Configuration.dll, without the part needing the reference to System.Xml.dll. After this, they compile System.Xml.dll the normal way. Now comes the magic. They recompile System.configuration.dll, with the part needing the reference to System.Xml.dll. Now there's a successful compilation with the circular reference.

In short:

  • A is compiled without the code
    needing B and the reference to B.
  • B is compiled.
  • A is recompiled.
凶凌 2024-12-09 07:52:36

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

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

RBarryYoung and Dykam are onto something. Microsoft uses internal tool which uses ILDASM to disassemble assemblies, strip all internal/private stuff and method bodies and recompile IL again (using ILASM) into what is called 'dehydrated assembly' or metadata assembly. This is done every time public interface of assembly is changed.

During the build, metadata assemblies are used instead of real ones. That way cycle is broken.

忆伤 2024-12-09 07:52:36

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

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

  1. csc /target:library 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 {
    }
}

It can be done the way Dykam described but Visual Studio blocks you from doing it.

You'll have to use the command-line compiler csc.exe directly.

  1. csc /target:library 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 {
    }
}
傲影 2024-12-09 07:52:36

只要您不使用项目引用,在 Visual Studio 中就很容易做到这一点...尝试以下操作:

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

所以这就是你如何做到的。但说真的......你从来没有在真正的项目中这样做过!如果你这样做,圣诞老人今年不会给你带来任何礼物。

Its pretty easy to do in Visual Studio as long as you don't use project references... Try this:

  1. Open visual studio
  2. Create 2 Class Library projects "ClassLibrary1" & "ClassLibrary2".
  3. Build
  4. From ClassLibrary1 add a reference to ClassLibrary2 by browsing to the dll created in step 3.
  5. From ClassLibrary2 add a reference to ClassLibrary1 by browsing to the dll created in step 3.
  6. Build again (Note: if you make changes in both projects you would need to build twice to make both references "fresh")

So this is how you do it. But seriously... Don't you EVER do it in a real project! If you do, Santa wont bring you any presents this year.

梦在深巷 2024-12-09 07:52:36

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

I guess it could be done by starting with an acyclic set of assemblies and using ILMerge to then coalesce the smaller assemblies into logically related groups.

ˇ宁静的妩媚 2024-12-09 07:52:36

嗯,我从未在 Windows 上做过它,但我在许多作为它的实际祖先的编译链接 RTL 环境上做过它。您要做的就是首先制作没有交叉引用的存根“目标”,然后链接,然后添加循环引用,然后重新链接。链接器通常不关心循环引用或后续引用链,它们只关心能够自行解析每个引用。

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

  1. 链接 A,但不带任何对 B 的引用。
  2. 链接 B 带有 对 A 的引用。
  3. 链接 A,添加引用 B.

Dykam 提出了一个很好的观点,它是编译的,而不是在 .Net 中链接,但原理仍然相同:使用导出的入口点创建交叉引用源,但除了其中一个之外,所有源都有自己的引用给其他人打断 出去。像这样构建它们。然后,取消外部引用的存根并重建它们。即使没有任何特殊工具,这种方法也应该有效,事实上,这种方法已经在我尝试过的每个操作系统上都有效(大约有 6 个)。显然,自动化的东西将会有很大的帮助。

Well, I've never done it on Windows, but I have done it on a lot of the compile-link-rtl environments that served as the practical progenitors for it. What you do is first make stub "targets" without the cross-references then link, then add the circular references, then re-link. The linkers generally do not care about circular refs or following ref chains, they only care about being able to resolve each reference on it's own.

So if you have two libraries, A and B that need to reference each other, try something like this:

  1. Link A without any refs to B.
  2. Link B with refs to A.
  3. Link A, adding in the refs to B.

Dykam makes a good point, It's compile, not link in .Net, but the principle remains the same: Make your cross-referenced sources, with their exported entry points, but with all but one of them having their own references to the others stubbed out. Build them like that. Then, unstub the external references and rebuild them. This should work even without any special tools, in fact, this approach has worked on every operating system that I have ever tried it on (about 6 of them). Though obviously something that automates it would be a big help.

獨角戲 2024-12-09 07:52:36

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

One possible approach is to use conditional compilation (#if) to first compile a System.dll that doesn't depend on those other assemblies, then compile the other assemblies, and at last recompile System.dll to include the parts depending on Xml and Configuration.

同尘 2024-12-09 07:52:36

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

Technically, it's possible that these were not compiled at all, and assembled by hand. These are low level libraries, after all.

很酷又爱笑 2024-12-09 07:52:36

同意。
asmmeta.exe 与 ildasm 类似,但省略了所有 IL(仅 ret)和一些私有变量,尽管有时需要私有变量(如结构大小)。

更普遍的想法是多遍构建,微软一直严重依赖它。

在实际上没有它们的系统中,精简的 ildasm 输出可以被视为“头”文件。

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

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

Agreed.
asmmeta.exe is like ildasm, but omits all the IL (just ret) and some privates, though sometimes privates are needed like for struct sizes.

The more general idea is that of a multi-pass build, which Microsoft has relied on heavily forever.

The stripped down ildasm output can be thought as as "header" file, in a system that does not really have them.

First visit each directory (with massive parallelism!) running ilasm.
Then visit each directory (again with massive parallelism) running csc.
After csc, in the same pass, run the like-ildasm tool, output back the original "headers".
Compare them. If there are any mismatches, the build is broken.
A developer failed to update the header.
It is too late to just patch it up, without restarting the build (perhaps with a proper dependency graph, most directories will not be affected).

This is also a way to upgrade versions easily.
The like-ilasm code can have names for version numbers.
Though this is really a minor outcome of a multi-pass build.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文