程序集引用的“特定版本”属性在 Visual Studio 中究竟是如何工作的?

Posted

技术标签:

【中文标题】程序集引用的“特定版本”属性在 Visual Studio 中究竟是如何工作的?【英文标题】:How exactly does the "Specific Version" property of an assembly reference work in Visual Studio? 【发布时间】:2014-07-24 04:48:11 【问题描述】:

今天,我仔细研究了 Visual Studio 2010 中程序集引用的“特定版本”属性。经过一些意外结果的实验​​后,我开始尽可能多地了解该属性的工作原理。即使如此,在我看来,也没有所有的答案,所以这是我尝试自我回答这个问题:

程序集引用的“特定版本”属性究竟如何在 Visual Studio 中工作?

【问题讨论】:

【参考方案1】:

这是一个编译时属性!

要知道的最重要的事情之一是“特定版本”是在编译时而不是在运行时生效的属性。

这是怎么回事?

构建项目时,需要解析项目的程序集引用,以便找到构建系统应使用的物理程序集。如果执行“特定版本”检查(参见“何时检查“特定版本”?”一节),它会影响程序集解析过程的结果:

构建系统找到它可能使用的物理程序集 构建系统将物理程序集的版本与 .csproj 文件中存储的程序集版本进行比较,以供程序集参考 如果两个程序集版本完全相同,则解析过程成功,找到的物理程序集用于构建 如果两个程序集版本不匹配,则丢弃物理程序集并通过定位下一个潜在程序集继续解决过程 如果找不到更多潜在的物理程序集,解析过程将失败。这会导致编译器警告(警告 MSB3245)告诉您无法解析引用。 有趣的是,然后构建继续!如果代码没有对程序集的实际引用,则构建成功(带有前面提到的警告)。如果代码有引用,则构建失败并出现错误,看起来好像代码使用了未知类型或命名空间。构建真正失败的唯一迹象是警告 MSB3245。

程序集的解析顺序

程序集解析过程定位潜在程序集的顺序如下:

    .csproj 文件中 <HintPath> 元素引用的程序集 项目输出路径 GAC

请注意,如果 GAC 中存在多个版本的程序集,则解析过程首先会尝试解析为具有最高版本的程序集。这仅在未进行“特定版本”检查时才重要。

什么时候勾选“特定版本”?

Visual Studio 根据 .csproj 文件中的两条信息决定是否执行“特定版本”检查:

<SpecificVersion> 元素的存在与否及其值(如果存在) 程序集引用中是否存在版本信息

这是带有版本信息的典型程序集引用的样子:

<Reference Include="Foo, Version=1.2.3.4, Culture=neutral, processorArchitecture=MSIL">
  <SpecificVersion>True</SpecificVersion>
  <HintPath>..\..\Bar\Foo.dll</HintPath>
</Reference>

这就是程序集参考的样子没有版本信息:

<Reference Include="Foo">
[...]

下表显示了何时执行“特定版本”检查,何时不执行。

                   |     Version information
                   |  Present       Not present
-------------------+------------------------------
<SpecificVersion>  |
- Present(=True)   |  1.Yes         2.Yes (check always fails)
- Present(=False)  |  3.No          4.No
- Not present      |  5.Yes         6.No

这里令人惊讶的是,如果&lt;SpecificVersion&gt; 和版本信息都不存在(案例6),则不会执行任何检查。我本来希望执行检查并且总是失败(与案例 2 相同),因为据我了解,&lt;SpecificVersion&gt; 的缺失意味着默认值“True”。这可能是我在其中进行测试的 Visual Studio 2010 的一个怪癖。

当您在 Visual Studio UI 中检查程序集引用的属性时(选择引用并按 F4),您看到的“特定版本”属性值会告诉您 Visual Studio 是否要执行“特定版本”检查。在案例 6 中,UI 将显示“True”,尽管 .csproj 文件中不存在 &lt;SpecificVersion&gt; 元素。

“复制本地”的副作用

如果“复制本地”属性设置为“真”,但程序集解析过程由于“特定版本”检查而失败,则不会复制任何程序集。

参考资料

What you need to know about referenced assemblies in VS2005(blogs.msdn.com 文章) What's New in .NET 2.0 for Assemblies and Versioning?(codemag.com 文章复制了上述 MSDN 文章,只包含措辞,但包含一些屏幕截图和有关程序集版本控制的其他信息)

【讨论】:

“在案例 6 中,UI 将显示“True”,尽管 .csproj 文件中不存在 元素。” - 似乎是默认值是。将 UI 中的特定版本切换为 True 后,&lt;SpecificVersion&gt; 标记被完全省略,之前的值为 False @herzbube - 我认为 Visual Studio > Project Properties 窗口中“特定版本”的含义与您在此处所说的相反(这与您所期望的相反) . Visual Studio 表示“特定版本”的值(true 或 false)“指示是否可以不考虑解决程序集解析的多目标规则”。 @herzbube 关于决议顺序,GAC 不是第一个检查的地方吗?还是仅适用于运行时?这不会造成编译时和运行时不同步吗? @joe 在我写这篇文章的时候,我确信顺序和我描述的一样。如果您对更现代的 Visual Studio 版本中的当前行为有疑问,请自行调查。关于你提到的“不同步”——我看不出这怎么可能是个问题。编译器会将引用嵌入到它使用的任何程序集,然后也将在运行时使用。如果这不是您期望的参考,那么您的构建系统设置不当。如果您需要对该主题进行更多详细说明,请提出一个新问题 - cmets 是一种糟糕的问答格式。【参考方案2】:

当您添加引用时,Visual Studio 会在项目文件中记录程序集的 [AssemblyVersion]。这个很重要。例如,如果您在一年后创建一个错误修复程序,那么您希望确保使用 exact 相同版本的参考重新构建项目,以便它是一个真正的插件。如果引用程序集已更改,您将收到错误消息。

但这并不总是可取的。一些程序员让汇编版本自动增加,每次重建时都会生成一个新版本。即使程序集的公共接口从未改变。有些人通过使用 Nuget 来获取库来配置他们的项目,并让它在有新版本可用时自动更新库。他们希望将 Specific Version 属性设置为 False 以抑制编译错误。

了解后果非常重要,您确实需要重新部署程序的整个构建以避免发生事故。运行时的版本不匹配会使程序崩溃,只能在 .config 文件中使用 &lt;bindingRedirect&gt; 来抑制,这是有风险的。

【讨论】:

感谢您提供信息为什么“特定版本”很重要,这是我在回答中涉及的纯机械方面的好伴侣。 @Hans Passant - 您的最后一段是否适用于 SpecificVersion True 或 False?我认为这些是您将其设置为 true 时的后果。 SpecificVersion 仅适用于构建您的应用程序。在运行时,CLR 始终坚持与参考程序集版本号完全匹配。如果您在构建时使用了较新的版本,那么它也需要在运行时使用较新的版本。 当心 VS2013 和 .Net 4.5.1 AutoGenerateBindingRedirects 即使您告诉它使用特定版本,它们也可能会将 dll 绑定重定向到较新的版本 @HansPassant 我认为当程序集没有强名称签名时,CLR 不会考虑 [AssemblyVersion]

以上是关于程序集引用的“特定版本”属性在 Visual Studio 中究竟是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章

Visual Studio 8 中程序集引用的 Aliases 属性有啥用

特定版本为 False 时的特定于版本的程序集引用

如何在 Visual Studio 中将源附加到引用的程序集

Visual Studio 2010 程序集签名:尝试引用不存在的令牌

Visual Studio 2010 程序集签名:尝试引用不存在的令牌

在 Visual Studio 2013 中,如何将代码分析限制为我的代码而不是引用的程序集?