【问题标题】:Is C# compile/build an incremental process?C# 编译/构建是增量过程吗?
【发布时间】:2017-05-17 03:58:30
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • 您可以考虑编写一个 Windows 批处理文件,您可以在其中提及所有依赖项和构建文件夹并一次性构建它们。
  • Eric Lippert 早在 2010 年写了一篇题为 How many passes? 的好帖子:“C# 语言不要求在使用之前发生声明......对用户的影响是你无法到达仅重新编译更改文件时更改的 IL ... 另一种看待这一点的方式是,C# 中重新编译的“粒度”是在项目级别,而不是文件级别”
  • 在 Visual Studio 的解决方案资源管理器窗口中,右键单击解决方案并查看项目/解决方案构建顺序。这也显示了项目的依赖关系。

标签: c# build-process


【解决方案1】:

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

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

设置:

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

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

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

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

【讨论】:

  • 您的意思可能是“再次优化构建A”而不是“再次优化构建B”。另一个想法:在大多数情况下,以这种方式优化可能比重新编译更耗时。
  • @StefanSteinegger 哎呀!谢谢,已解决。
【解决方案2】:

你说得对。

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

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

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

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

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

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

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

【讨论】:

  • 非常感谢。 Java 将重新编译更改的文件,然后打包为 .jar。它重用以前的工作输出(未更改文件的 .class)。 C# 不重用任何以前的工作输出。它没有任何中间文件,就像 Java 的 .class 和 C++ 的 .obj 一样。所以在这一点上,我觉得 C# 不做增量构建过程。我不明白为什么 C# 不使用以前的工作输出来加速构建过程。
  • 根据我的经验,Java 构建过程(使用 Android Studio)比 C#(使用 Visual Studio)花费更多时间。我的 Visual Studio 项目比 Java 项目大几个数量级。我对 Java 的第一次体验更多的是:“这需要很长时间:我的配置有什么问题。”
【解决方案3】:

好的..您实际上是在正确的轨道上..如果解决方案中有 3 个项目,您可以将一个项目的引用提供给另一个。就是说项目A依赖项目B又依赖项目C..

当您构建一个项目时,所有构建的文件和 dll(来自构建后事件)最终都在该项目的 bin 文件夹中。

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

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

【讨论】:

  • 谢谢。我只是觉得这种构建方法并不聪明。在 C++ 中,以前的工作(目标文件)将在新的构建过程中重用。在 Java 中,不需要重新构建其他文件和项目。如果我在项目 C 中更改一个文件,Java 将构建非常快(只有一个文件),C++ 会变慢(使用一些以前的工作输出,然后做链接),C# 将重建所有。在我的工作中,我总是改变一些东西,重建,调试。我等不及建造了。所以我不敢相信 C# 这么愚蠢。我怀疑我不明白。
  • 我正要更新这个但@InBetween 打败了我......很好的答案.. :)
【解决方案4】:

我有一个包含许多项目甚至更多跨项目引用的解决方案。

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

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

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

我从 redgate 发现了.NET Daemon。这是一个更改构建过程的工具,并且仅当来自 C 的公共 API 发生更改时才会重建 B。这会对您的构建过程产生巨大影响。我想说,根据我的个人经验,它可以节省大约 80% 的构建时间。

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

Visual Studio 2015 将引入微软新的 Roslyn 编译器, 我们认为这些改进使 .NET Demon 变得多余。

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

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

【讨论】:

  • 谢谢。我和你有同样的问题。我会尝试VS2015。
  • 如果有帮助,请告诉我。我也计划迁移到 2015 年。但是如果 .NET Deamon 将消失,这将是一个劣势,除非 VS 2015 进行一些类似的改进。
【解决方案5】:

所有版本的 C#(真的是 MSBuild)都支持非依赖项目的增量编译。

VS2003 中的 C# 支持增量编译 within projects。有一个 uservoice suggestion 将该功能引入最新的 C# 版本。

【讨论】:

    【解决方案6】:

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

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

    【讨论】:

      猜你喜欢
      • 2021-11-22
      • 1970-01-01
      • 2014-07-28
      • 2014-06-07
      • 2015-07-23
      • 1970-01-01
      • 1970-01-01
      • 2016-12-13
      • 1970-01-01
      相关资源
      最近更新 更多