何时创建和分发“参考装配”?

时间:2018-04-11 14:20:58

标签: c# .net-assembly roslyn c#-7.1

C# 7.1引入了一些新的命令行参数来帮助创建“引用程序集”。通过documentation,它输出一个组件:

  

将其方法体替换为单个throw null体,但包括除匿名类型之外的所有成员。

我找到了an interesting note that it is more stable on changes

  

这意味着它的变化频率低于完整程序集 - 许多常见的开发活动不会改变界面,只会改变实现。这意味着增量构建可以更快 - ...

it is probably necessary for roslyn itself ..

  

我们将介绍第二个概念,即“参考组件”(也称为骨架组件)。 [---] 它们将用于构建场景。

..无论那些“构建场景”是针对Roslyn的。

据我所知,对于普通的.NET程序集用户来说,这样的程序集可能更小,加载反射速度稍快。好的,但是:

  • 通常你也关心执行,而实现程序集已经包含了来自引用程序集的所有数据,
  • 通常你不关心加载时的微小性能差异,
  • 最重要的是 - 通常你根本没有可用的(分发的)精简参考组件。

它的用处似乎很小。

所以,我想知道通用程序集生成器方面的事情 - 何时应该考虑明确使用这些新的编译器标志来创建引用程序集?它在Roslyn本身之外是否有任何实际用途?

1 个答案:

答案 0 :(得分:1)

这个功能的动机确实是构建场景,但它们并不是特定于Roslyn; 他们 构建方案

构建项目时,构建引擎(MSBuild)需要确定构建的每个输出是否与其输入相关。例如,如果你没有改变任何东西并且只是连续两次运行构建,那么第二次不需要调用C#编译器:程序集已经是正确的。

参考程序集允许在更多场景中跳过程序集的编译步骤,因此您的构建可以更快。我想一个例子可以帮助说明。

假设您的解决方案包含的B.exe取决于A.dll

B的编译器命令行看起来像

csc.exe /out:B.exe /r:..\A\bin\A.dll Program.cs

它的输入将是

  • B的来源(Program.cs
  • A。
  • 的装配

如果更改A的源并构建解决方案,则编译器必须针对A运行,生成新的A.dll。然后,由于A.dll是B编译的输入,因此B也必须重新编译。

使用A的参考组件稍微改变

csc.exe /out:B.exe /r:..\A\bin\ref\A.dll Program.cs

A的输入现在是引用程序集,而不是它的实现/正常程序集。

由于引用程序集小于完整程序集,因此对构建时间的影响很小。但这还不足以证明这一功能。重要的是编译器只关心传入引用的公共API表面。如果程序集的内部实现详细信息已更改,则不需要重新编译引用它的程序集以获取新行为。正如@Hans Passant在评论中提到的那样,这就是.NET Framework本身如何提供兼容的性能改进以及对未更改的用户代码的错误修复。

参考程序集功能的好处来自于使用它们完成的MSBuild工作。假设您更改了A中的内部实现细节但未更改其公共接口。在下一个版本中,

  • 编译器必须为A运行,因为A的源文件已更改。
  • 编译器同时发出A.dll(带有已更改的实现)和ref\A.dll,这与前一个引用程序集相同。
  • 由于ref\A.dll与之前的输出相同,因此不会将其复制到A的输出文件夹。
  • 当B&#39编译器运行的时候,它看到它的输入都没有改变 - 既不是B自己的代码,也不是A的引用程序集,所以编译器没有& #39; t必须跑。
  • B然后将更新后的A.dll复制到其输出中,并准备好以新行为运行。

在大型解决方案中,跳过下游编译的效果会更复杂 - 更改{ProjectName}.Utilities.dll中的注释不再需要构建所有内容!

许多更改涉及更改公共API表面和内部实现,因此此更改不会加快所有构建,但它确实加快了许多构建。