System.Runtime.Versioning.ComponentGuaranteesAttribute 类

本文提供了此 API 参考文档的补充说明。

ComponentGuaranteesAttribute组件和类库的开发人员使用该组件和类库来指示其库使用者可以在多个版本中期望的兼容性级别。 它指示将来版本的库或组件不会中断现有客户端的保证级别。 客户端可以使用 ComponentGuaranteesAttribute 作为设计自己的接口的辅助工具,以确保版本之间的稳定性。

注释

公共语言运行时(CLR)不会以任何方式使用此属性。 其价值在于正式记录组件作者的意图。 编译时工具还可以使用这些声明来检测编译时错误,否则会破坏声明的保证。

兼容性级别

ComponentGuaranteesAttribute 支持以下兼容性级别,这些级别由 ComponentGuaranteesOptions 枚举中的成员表示:

  • 没有版本到版本兼容性(ComponentGuaranteesOptions.None)。 用户可以预期将来的版本会破坏现有的客户端。 有关详细信息,请参阅本文后面的 “无兼容性 ”部分。

  • 并行版本到版本兼容性 (ComponentGuaranteesOptions.SideBySide)。 当在同一应用程序域中加载多个版本的程序集时,该组件已经过测试,能正常工作。 通常,将来的版本可能会中断兼容性。 但是,在进行中断性变更时,旧版本不会修改,而是与新版本一起存在。 并行执行是使现有客户端在进行重大更改时正常工作的预期方式。 有关详细信息,请参阅本文后面的 并行兼容性 部分。

  • 稳定的版本间兼容性 (ComponentGuaranteesOptions.Stable)。 将来的版本不应中断客户端,不需要并行执行。 但是,如果客户端无意中损坏,则可能可以使用并行执行来解决问题。 有关详细信息,请参阅 “稳定兼容性 ”部分。

  • Exchange 版本间兼容性(ComponentGuaranteesOptions.Exchange)。 请务必特别注意确保将来的版本不会中断客户端。 客户端应仅在接口签名中使用这些类型,这些类型用于与其他独立于彼此部署的程序集通信。 这些类型的一个版本应位于给定的应用程序域中,这意味着如果客户端中断,并行执行无法解决兼容性问题。 有关详细信息,请参阅 Exchange 类型兼容性 部分。

以下各节更详细地讨论了每种保证级别。

无兼容性

将组件 ComponentGuaranteesOptions.None 标记为指示提供程序无法保证兼容性。 客户端应避免对公开接口产生任何依赖项。 此级别的兼容性对于试验性类型或公开的类型非常有用,但仅适用于始终同时更新的组件。 None 显式指示外部组件不应使用此组件。

并行兼容性

将一个组件标记为 ComponentGuaranteesOptions.SideBySide 表示该组件已经过测试,可以在将多个版本的程序集加载到同一应用程序域时正常工作。 只要对版本号较大的程序集进行重大更改,就允许进行重大更改。 绑定到旧版本的程序集的组件应继续绑定到旧版本,其他组件可以绑定到新版本。 还可以通过破坏性地修改旧版本来更新声明为 SideBySide 的组件。

稳定兼容性

将某种类型标记为ComponentGuaranteesOptions.Stable意味着该类型应在各个版本之间保持稳定。 但是,也可以让稳定类型的并行版本存在于同一应用程序域中。

稳定类型保持高水平的二进制兼容性。 因此,提供者应避免对稳定类型进行重大更改。 可以接受以下类型的更改:

  • 将专用实例字段添加到或从类型中删除字段,前提是这不会中断序列化格式。
  • 将不可序列化的类型更改为可序列化类型。 (但是,无法将可序列化类型更改为不可序列化的类型。
  • 从方法中引发派生程度更高的新异常。
  • 提高方法的性能。
  • 更改返回值的范围,只要更改不会对大多数客户端产生不利影响。
  • 修复严重的 bug(如果业务理由较高且受影响的客户端数量较低)。

由于新版稳定组件不应中断现有客户端,因此应用程序域中通常只需要一个稳定组件的版本。 但是,这不是一项要求,因为稳定类型不用作所有组件都同意的已知交换类型。 因此,如果稳定组件的新版本无意中中断了某些组件,并且如果其他组件需要新版本,则可以通过加载旧组件和新组件来解决此问题。

Stable 提供比 None 更强大的版本兼容性保证。 这是多版本组件的常见默认值。

Stable 可以与 SideBySide 组合在一起,这说明该组件不会破坏兼容性,并且在给定的应用程序域中加载多个版本时也能正常工作。

将类型或方法标记为 Stable后,可以将其升级到 Exchange。 但是,不能将其降级为 None

交换类型兼容性

将一种类型标记为 ComponentGuaranteesOptions.Exchange 提供比 Stable 更强的版本兼容性保证,应当应用于所有类型中最稳定的类型。 这些类型旨在用于在跨所有组件边界(时间(任何版本的 CLR 或任何版本的组件或应用程序)和空间(跨进程、一个进程中跨 CLR、一个 CLR 中跨应用程序域))独立构建的组件之间进行交换。 如果对交换类型进行了重大更改,则无法通过加载该类型的多个版本来解决此问题。

只有当问题非常严重(例如涉及严重安全问题)或中断的概率非常低时(即,如果行为已经以一种不可预见的方式被破坏,代码不可能依赖于这样的行为),才应更改交换类型。 可以对交换类型进行以下类型的更改:

  • 添加新接口定义的继承。

  • 添加新的私有方法,用于实现新继承的接口定义的方法。

  • 添加新的静态字段。

  • 添加新的静态方法。

  • 添加新的非虚拟实例方法。

以下更改被视为重大更改,并且不允许对基元类型进行这些更改:

  • 更改序列化格式。 需要版本容错序列化。

  • 添加或删除专用实例字段。 这有可能改变类型的序列化格式,并破坏使用反射的客户端代码。

  • 更改类型的可序列化性。 不可序列化的类型不能被转换为可序列化的类型,反之亦然。

  • 从方法中引发其他异常。

  • 更改方法的返回值的范围,除非成员定义引发这种可能性,并清楚地指示客户端应如何处理未知值。

  • 修复大多数错误。 该类型的使用者将依赖于现有行为。

经过 Exchange 保证标记的组件、类型或成员,不能更改为 StableNone

通常,交换类型是基本类型(例如 .NET 中的Int32String)和接口(例如IList<T>IEnumerable<T>IComparable<T>),这些类型和接口通常用于公共接口。

交换类型可能只公开那些也标有 Exchange 兼容性的其他类型。 此外,交换类型不能依赖于容易更改的 Windows API 的行为。

组件保证

下表指示组件的特征和使用如何影响其兼容性保证。

组件特征 Exchange 稳定 并排 没有
可以在独立版本的组件之间的接口中使用。 Y
可由独立进行版本控制的程序集(私下)使用。 Y Y Y
单个应用程序域中可以有多个版本。 Y Y Y
可以做出重大更改 Y
经过测试,确保多个版本的程序集可以一起加载。 Y
可以就地进行重大更改。 Y
可以就地进行非常安全的非中断性服务更改。 Y Y Y Y

应用此属性

您可以将 ComponentGuaranteesAttribute 应用于程序集、类型或类型成员。 其应用程序是分层的。 也就是说,默认情况下,程序集级别的特性的 Guarantees 属性所定义的保证会定义程序集中所有类型以及这些类型中所有成员的保证。 同样,如果保证应用于类型,则默认情况下,它也适用于该类型的每个成员。

可以通过将 ComponentGuaranteesAttribute 应用于各个类型和类型成员来替代该继承的保证。 然而,覆盖默认设置的保证只能削弱原有的保证效力,而不能增强它。 例如,如果某个程序集标记为 None 保证,则其类型和成员不具有兼容性保证,并且应用于程序集中的类型或成员的任何其他保证都会被忽略。

测试保证

Guarantees 属性返回 ComponentGuaranteesOptions 枚举的成员,该成员用 FlagsAttribute 属性标记。 这意味着应通过屏蔽可能未知的标志来测试你感兴趣的标志。 例如,以下示例测试类型是否标记为 Stable

// Test whether guarantee is Stable.
if ((guarantee & ComponentGuaranteesOptions.Stable) == ComponentGuaranteesOptions.Stable)
   Console.WriteLine($"{typ.Name} is marked as {guarantee}.");
' Test whether guarantee is Stable.
If (guarantee And ComponentGuaranteesOptions.Stable) = ComponentGuaranteesOptions.Stable Then
   Console.WriteLine("{0} is marked as {1}.", typ.Name, guarantee)
End If

以下示例测试类型是否标记为 StableExchange

// Test whether guarantee is Stable or Exchange.
if ((guarantee & (ComponentGuaranteesOptions.Stable | ComponentGuaranteesOptions.Exchange)) > 0)
   Console.WriteLine($"{typ.Name} is marked as Stable or Exchange.");
' Test whether guarantee is Stable or Exchange.
If (guarantee And (ComponentGuaranteesOptions.Stable Or ComponentGuaranteesOptions.Exchange)) > 0 Then
   Console.WriteLine("{0} is marked as Stable or Exchange.", typ.Name, guarantee)
End If

以下示例测试类型是否标记为 None (即不标记为Stable 且不标记为Exchange)。

// Test whether there is no guarantee (neither Stable nor Exchange).
if ((guarantee & (ComponentGuaranteesOptions.Stable | ComponentGuaranteesOptions.Exchange)) == 0)
   Console.WriteLine($"{typ.Name} has no compatibility guarantee.");
' Test whether there is no guarantee (neither Stable nor Exchange).
If (guarantee And (ComponentGuaranteesOptions.Stable Or ComponentGuaranteesOptions.Exchange)) = 0 Then
   Console.WriteLine("{0} has no compatibility guarantee.", typ.Name, guarantee)
End If