C# compile/build 是增量过程吗?

Is C# compile/build an incremental process?

我们的解决方案包含大量 C# 项目。它们之间存在复杂的依赖关系,例如。项目A/B/C,A依赖B,B依赖C。如果我更改项目C中的一个文件,然后重建解决方案,项目A,B,C将一起重建。

在C++中,构建包含编译和link两个过程。如果我更改项目C中的一个文件,然后我构建解决方案,A和B中的相关文件将被编译(其他文件不会被编译,他们的.obj将在link过程中被重用),然后做 link。

在java中,只会重新编译工程C中的变化文件,其他文件将保留,然后打包成.jar。它重用以前的工作输出(未更改文件的 .class)。

总之,C# 不重用任何以前的工作输出。它没有任何中间文件,就像 Java 的 .class 和 C++ 的 .obj. 所以在这一点上,我觉得 C# 不执行增量构建过程。一些小的改变会导致一个大的构建过程。我不明白为什么 C# 不使用以前的工作输出来加速构建过程。

我不确定我对C#compile/build过程的理解是否正确。你能帮忙解释一下吗?非常感谢。

好的..您实际上是在正确的轨道上..如果解决方案中有 3 个项目,您可以将一个项目作为另一个项目的参考。这意味着项目A依赖于项目B依赖于项目C..

构建项目时,所有构建的文件和 dll(来自 post 构建事件)最终都位于该项目的 bin 文件夹中。

因此,当您构建项目 A 时,项目 C 将首先构建(因为 A->B->C)。项目 B 使用项目 C 的构建组件并创建自己的组件。并且项目 A 使用 B 和 C 的组件并创建自己的组件。

因此,如果你只构建项目A,如果引用正确,你将在项目A的bin文件夹中看到B和C的所有构建文件。

C# 编译器进行增量编译,我不确定您从哪里得知它没有。也许,由于您的解决方案的复杂性,您没有正确理解依赖关系,您认为不需要重新编译的项目实际上是必要的。

检查编译器行为的最佳方法是创建一个简单的虚拟解决方案并试用它:

设置:

  1. 创建一个空的 Visual Studio C# 解决方案。
  2. 添加任意两个项目,AB
  3. 使项目 B 成为项目 A 中的引用。
  4. B中实现classFooInB并在ABarInA.
  5. 中的另一个class中使用它

现在让我们尝试一下这个设置:

  1. 编译解决方案。您将看到两个项目都可以编译。
  2. 再次编译解决方案。您将看到 none 个项目编译,都是最新的。
  3. 更改BarInA中的实现并重新编译。你 将看到 只有 个项目编译,A。没有必要 再次编译 B,因为没有任何变化。
  4. 更改FooInB中的实现,最后编译一次。您将看到两个项目都可以编译。此行为是正确的,A 取决于 B,因此 B 中的任何更改都必然需要 A 再次重新编译以确保它指向最新版本的 B。在理论世界中,C# 编译器可以检测 B 中的更改是否对 A 没有影响,因此可以 "optimize" 构建 A 再次,这将是一个噩梦场景,其中每个项目都可能引用不同的和过时的程序集版本。

也就是说,我想指出,据我所知,C# 编译器只会在 项目 级别执行增量编译。我不知道任何给定程序集中 class 级别的任何增量编译优化。对编译器的内部工作原理有更多了解的人可能能够澄清这种行为。

你说的有点对。

如果项目 A 依赖于项目 B。依赖项目 B 的更改确实需要重新编译项目 A。

如果项目 B 依赖于项目 C。项目 B 中的更改不会使项目 C 重新编译。

在Java中,一个class/file被编译成一个.class文件。因此,当单个文件更改时,不会重新编译整个项目。

在分发之前,这些 .class 文件被合并到一个 .jar 中。如果单个 .class 文件更改,整个 .jar 也需要重新组装。

在 .Net 中,您会立即将项目编译为程序集(.dll 或 .exe),因此单个文件更改需要重新编译整个程序集。

在 .jar(或 .apk 等依赖项)用于 run/debug 应用程序的 Java 环境中可以看到相同的行为。整个 .jar 在开发过程中也需要。当单个文件更改时,整个项目将重新编译。

您始终可以在解决方案之外放置项目。并且只添加对它们生成的 .dll 的引用。这将最大限度地减少编译时间。

我有一个包含许多项目和更多 cross-project 参考的解决方案。

为简单起见,假设我有项目 A BC

  • A 是我的主要桌面 exe,它引用 BC
  • B 包含 windows 表格和引用 C
  • C 包含业务和数据库逻辑。

我的主要问题是:B 编译大约需要四十五秒。现在假设我只更改 C 中的一小行代码,编译器将编译 C,然后编译 B,而不是 A 由于我主要更改 C 中的代码,编译速度非常快,所以我总是必须等待 B 编译。

我从 redgate 发现了 .NET Daemon。这是一个改变构建过程的工具,只有在 C 的 public API 发生变化时才会重建 B。这会对您的构建过程产生巨大影响。我会说,根据我的个人经验,它可以节省大约 80% 的一天构建时间..

但是,我只是看了看,他们不再卖了:https://www.red-gate.com/products/dotnet-development/dotnet-demon/

Visual Studio 2015 will introduce Microsoft's new Roslyn compiler, with improvements which we believe make .NET Demon redundant.

我还在 Visual Studio 2013 年,但我认为对构建过程的初步观察和关于 NET 构建的其他答案可能不再适用于 Visual Studio 2015 年的 Roslyn。也许它的行为更像 dotnet-deamon build.

旁注:我的新项目使用 IOC 容器,所有内容都连接在主项目中,没有太多跨项目引用。这也将改进构建过程,因为几乎所有内容都可以并行构建。

所有版本的 C#(实际上是 MSBuild)都支持 non-dependent 个项目的增量编译。

VS2003 中的 C# 支持增量编译 within projects. There is a uservoice suggestion 以将该功能推进到最新的 C# 版本中。

我已经使用 Jenkins MSBuild 插件进行了测试,增量构建按预期工作,并且只会重新编译更改的项目。

我想要的是增量部署,只部署这些 changed/recompiled dll。现在我怎样才能有效地找到重新编译的dll。