面向 C# 开发人员的版本和更新注意事项
随着新功能被添加到 C# 语言中,兼容性成为一个重要的目标。 在几乎所有情况下,都可以使用新的编译器版本重新编译现有代码,不会出现任何问题。 .NET 运行时团队还有一个目标,即确保更新库的兼容性。 在几乎所有情况下,从具有更新库的更新运行时启动应用时,其行为与以前版本的行为完全相同。
用于编译应用的语言版本通常与项目中引用的运行时目标框架名字对象 (TFM) 匹配。 若要详细了解如何更改默认语言版本,请参阅标题为配置语言版本的文章。 此默认行为可确保最大的兼容性。
引入中断性变更时,它们被归为以下几类:
- 二进制中断性变更:使用新运行时启动时,二进制中断性变更会导致应用程序或库中出现不同的行为,包括可能发生故障。 必须重新编译应用才能合并这些更改。 现有二进制文件无法正常工作。
- 源中断性变更:源中断性变更会更改源代码的含义。 在使用最新语言版本编译应用程序之前,需要编辑源代码。 现有二进制文件将在较新的主机和运行时中正常运行。 请注意,对于语言语法,源中断性变更也是行为变更,如运行时中断性变更中所定义。
如果二进制中断性变更影响应用,就必须重新编译应用,但无需编辑任何源代码。 如果源中断性变更影响应用,现有二进制文件仍可在具有更新的运行时和库的环境中正常运行。 但是,必须进行源更改才能使用新的语言版本和运行时重新编译。 如果更改既是源中断性变更又是二进制中断性变更,则必须使用最新版本重新编译应用程序并更新源。
由于 C# 语言团队和运行时团队的目标是避免中断性变更,因此更新应用程序通常是更新 TFM 和重新生成应用的问题。 但对于公开分发的库,应仔细评估策略是否支持 TFM 和支持的语言版本。 你可能要使用最新版本中提供的功能创建一个新库,并且需要确保使用以前版本的编译器生成的应用可以使用它。 或者,你可能要升级现有库,但许多用户可能还没有升级版本。
在库中引入中断性变更
在库的公共 API 中采用新语言功能时,应评估采用该功能是否为库用户引入了二进制或源中断性变更。 在 public
或 protected
接口中未出现的针对内部实现的任何更改都是兼容的。
注意
如果使用 System.Runtime.CompilerServices.InternalsVisibleToAttribute 使类型能够看到内部成员,则内部成员可能会引入中断性变更。
二进制中断性变更要求用户重新编译其代码才能使用新版本。 例如,请考虑以下公共方法:
public double CalculateSquare(double value) => value * value;
如果将 in
修饰符添加到方法,则这是二进制中断性变更:
public double CalculateSquare(in double value) => value * value;
用户必须重新编译使用 CalculateSquare
方法的任何应用程序才能使新库正常工作。
源中断性变更要求用户在重新编译之前更改其代码。 例如,请考虑以下类型:
public class Person
{
public string FirstName { get; }
public string LastName { get; }
public Person(string firstName, string lastName) => (FirstName, LastName) = (firstName, lastName);
// other details omitted
}
在较新版本中,你想要利用为 record
类型生成的合成成员。 进行下列更改:
public record class Person(string FirstName, string LastName);
前面的更改要求对派生自 Person
的任何类型进行更改。 所有这些声明都必须将 record
修饰符添加到其声明中。
中断性变更的影响
向库添加二进制中断性变更时,将强制所有使用库的项目重新编译。 但这些项目中的源代码都不需要更改。 因此,中断性变更对每个项目的影响都相当小。
对库进行源中断性变更时,需要所有项目进行源更改才能使用新库。 如果必要的更改需要新的语言功能,则强制这些项目升级到你现在使用的语言版本和 TFM。 你需要为用户执行更多工作,并且他们还可能被迫升级。
所做的任何中断性变更的影响取决于依赖于库的项目数。 如果一些应用程序在内部使用库,你可以对所有受影响项目中的任何中断性变更做出响应。 但如果库是公开下载的,则应评估潜在影响并考虑替代方法:
- 可添加与现有 API 并行的新 API。
- 可考虑对不同的 TFM 使用并行生成。
- 可以考虑使用多目标。