何时创建和分发 "reference assemblies"?
When to create and distribute "reference assemblies"?
C# 7.1 introduced a few new command line parameters to help create "reference assemblies". By documentation 它输出一个程序集:
have their method bodies replaced with a single throw null body, but include all members except anonymous types.
我找到了 an interesting note that it is more stable on changes:
That means it changes less often than the full assembly--many common development activities don't change the interface, only the implementation. That means that incremental builds can be much faster- ...
还有 it is probably necessary for roslyn itself ..
We will be introducing a second concept, which is "reference assemblies" (also called skeleton assemblies). [---] They will be used for build scenarios.
.. 无论那些 "build scenarios" 是给 Roslyn 的。
据我所知,对于普通的 .NET 程序集用户来说,这样的程序集可能更小,加载反射的速度也稍快一些。好的,但是:
- 通常你也关心执行和实现程序集已经包含来自参考程序集的所有数据,
- 通常您并不关心加载时的微小性能差异,
- 最重要的是 - 通常您根本没有可用(分布式)的精简参考程序集。
它的用处似乎比较小众。
所以,我想知道通用程序集生产者方面的事情 - 什么时候应该明确考虑使用那些新的编译器标志来创建参考程序集?它在 Roslyn 本身之外有任何实际用途吗?
此功能的动机确实是构建场景,但它们并不特定于 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 的输入现在是它的 参考程序集 ,而不是它的 implementation/normal 程序集。
由于参考程序集小于完整程序集,因此它本身对构建时间的影响很小。但这还不足以证明此功能的合理性。重要的是 编译器只关心 passed-in 引用 的 public API 表面。如果程序集的内部实现细节发生了变化,引用它的程序集不需要重新编译来获取新的行为。正如@Hans Passant 在评论中提到的那样,这就是 .NET Framework 本身如何在未更改的用户代码上提供兼容的性能改进和错误修复。
引用程序集功能的好处来自为使用它们所做的 MSBuild 工作。假设您更改 A 中的内部实现细节但不更改其 public 接口。在下一个版本中,
- A 的编译器必须 运行,因为 A 的源文件已更改。
- 编译器同时发出
A.dll
(具有更改的实现)和 ref\A.dll
,这与之前的参考程序集相同。
- 由于
ref\A.dll
与之前的输出相同,因此不会复制到 A 的输出文件夹。
- 当 B 的编译器执行 运行 时,它发现其输入的 none 已更改——既不是 B 自己的代码,也不是 A 的参考程序集,因此编译器不会必须 运行.
- B 然后将更新后的
A.dll
复制到它的输出,并准备 运行 使用新的行为。
在大型解决方案中进行时,跳过下游编译的效果会更加复杂——更改 {ProjectName}.Utilities.dll
中的注释不再需要构建所有内容!
许多更改涉及更改 public API 表面和内部实现,因此此更改不会加速所有构建,但它确实加速了许多构建。
C# 7.1 introduced a few new command line parameters to help create "reference assemblies". By documentation 它输出一个程序集:
have their method bodies replaced with a single throw null body, but include all members except anonymous types.
我找到了 an interesting note that it is more stable on changes:
That means it changes less often than the full assembly--many common development activities don't change the interface, only the implementation. That means that incremental builds can be much faster- ...
还有 it is probably necessary for roslyn itself ..
We will be introducing a second concept, which is "reference assemblies" (also called skeleton assemblies). [---] They will be used for build scenarios.
.. 无论那些 "build scenarios" 是给 Roslyn 的。
据我所知,对于普通的 .NET 程序集用户来说,这样的程序集可能更小,加载反射的速度也稍快一些。好的,但是:
- 通常你也关心执行和实现程序集已经包含来自参考程序集的所有数据,
- 通常您并不关心加载时的微小性能差异,
- 最重要的是 - 通常您根本没有可用(分布式)的精简参考程序集。
它的用处似乎比较小众。
所以,我想知道通用程序集生产者方面的事情 - 什么时候应该明确考虑使用那些新的编译器标志来创建参考程序集?它在 Roslyn 本身之外有任何实际用途吗?
此功能的动机确实是构建场景,但它们并不特定于 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 的输入现在是它的 参考程序集 ,而不是它的 implementation/normal 程序集。
由于参考程序集小于完整程序集,因此它本身对构建时间的影响很小。但这还不足以证明此功能的合理性。重要的是 编译器只关心 passed-in 引用 的 public API 表面。如果程序集的内部实现细节发生了变化,引用它的程序集不需要重新编译来获取新的行为。正如@Hans Passant 在评论中提到的那样,这就是 .NET Framework 本身如何在未更改的用户代码上提供兼容的性能改进和错误修复。
引用程序集功能的好处来自为使用它们所做的 MSBuild 工作。假设您更改 A 中的内部实现细节但不更改其 public 接口。在下一个版本中,
- A 的编译器必须 运行,因为 A 的源文件已更改。
- 编译器同时发出
A.dll
(具有更改的实现)和ref\A.dll
,这与之前的参考程序集相同。 - 由于
ref\A.dll
与之前的输出相同,因此不会复制到 A 的输出文件夹。 - 当 B 的编译器执行 运行 时,它发现其输入的 none 已更改——既不是 B 自己的代码,也不是 A 的参考程序集,因此编译器不会必须 运行.
- B 然后将更新后的
A.dll
复制到它的输出,并准备 运行 使用新的行为。
在大型解决方案中进行时,跳过下游编译的效果会更加复杂——更改 {ProjectName}.Utilities.dll
中的注释不再需要构建所有内容!
许多更改涉及更改 public API 表面和内部实现,因此此更改不会加速所有构建,但它确实加速了许多构建。