.NET 中 API 突破性更改的权威指南 - A definitive guide to API-breaking changes in .NET

问题:

I would like to gather as much information as possible regarding API versioning in .NET/CLR, and specifically how API changes do or do not break client applications.我想收集尽可能多的关于 .NET/CLR 中 API 版本控制的信息,特别是 API 更改如何破坏或不破坏客户端应用程序。 First, let's define some terms:首先,让我们定义一些术语:

API change - a change in the publicly visible definition of a type, including any of its public members. API 更改- 类型的公开可见定义的更改,包括其任何公共成员。 This includes changing type and member names, changing base type of a type, adding/removing interfaces from list of implemented interfaces of a type, adding/removing members (including overloads), changing member visibility, renaming method and type parameters, adding default values for method parameters, adding/removing attributes on types and members, and adding/removing generic type parameters on types and members (did I miss anything?).这包括更改类型和成员名称、更改类型的基类型、从类型的已实现接口列表中添加/删除接口、添加/删除成员(包括重载)、更改成员可见性、重命名方法和类型参数、添加默认值对于方法参数,添加/删除类型和成员的属性,以及添加/删除类型和成员的泛型类型参数(我错过了什么?)。 This does not include any changes in member bodies, or any changes to private members (ie we do not take into account Reflection).这不包括成员团体的任何变化,或私人成员的任何变化(即我们不考虑 Reflection)。

Binary-level break - an API change that results in client assemblies compiled against older version of the API potentially not loading with the new version.二进制级中断- 一种 A​​PI 更改,导致针对旧版本 API 编译的客户端程序集可能不会随新版本一起加载。 Example: changing method signature, even if it allows to be called in the same way as before (ie: void to return type / parameter default values overloads).示例:更改方法签名,即使它允许以与以前相同的方式调用(即:void 返回类型/参数默认值重载)。

Source-level break - an API change that results in existing code written to compile against older version of the API potentially not compiling with the new version.源级中断- 一种 A​​PI 更改,导致编写的现有代码针对旧版本的 API 进行编译,可能无法使用新版本进行编译。 Already compiled client assemblies work as before, however.然而,已经编译的客户端程序集像以前一样工作。 Example: adding a new overload that can result in ambiguity in method calls that were unambiguous previous.示例:添加新的重载可能会导致之前明确的方法调用出现歧义。

Source-level quiet semantics change - an API change that results in existing code written to compile against older version of the API quietly change its semantics, eg by calling a different method.源级安静语义更改- API 更改导致现有代码编写以针对旧版本的 API 进行编译,例如通过调用不同的方法悄悄更改其语义。 The code should however continue to compile with no warnings/errors, and previously compiled assemblies should work as before.然而,代码应该继续编译而不会出现警告/错误,并且之前编译的程序集应该像以前一样工作。 Example: implementing a new interface on an existing class that results in a different overload being chosen during overload resolution.示例:在现有类上实现新接口,导致在重载解析期间选择不同的重载。

The ultimate goal is to catalogize as many breaking and quiet semantics API changes as possible, and describe exact effect of breakage, and which languages are and are not affected by it.最终目标是对尽可能多的破坏性和安静语义 API 更改进行分类,并描述破坏的确切影响,以及哪些语言受到和不受影响。 To expand on the latter: while some changes affect all languages universally (eg adding a new member to an interface will break implementations of that interface in any language), some require very specific language semantics to enter into play to get a break.扩展后者:虽然一些更改普遍影响所有语言(例如,向接口添加新成员将破坏该接口在任何语言中的实现),但有些更改需要非常特定的语言语义才能发挥作用才能中断。 This most typically involves method overloading, and, in general, anything having to do with implicit type conversions.这通常涉及方法重载,并且通常涉及与隐式类型转换有关的任何事情。 There doesn't seem to be any way to define the "least common denominator" here even for CLS-conformant languages (ie those conforming at least to rules of "CLS consumer" as defined in CLI spec) - though I'll appreciate if someone corrects me as being wrong here - so this will have to go language by language.即使对于符合 CLS 的语言(即那些至少符合 CLI 规范中定义的“CLS 消费者”规则的语言),似乎也没有任何方法可以在这里定义“最小公分母”——尽管我会很感激有人纠正我在这里错了 - 所以这将不得不逐个语言地进行。 Those of most interest are naturally the ones that come with .NET out of the box: C#, VB and F#;最感兴趣的自然是 .NET 自带的那些:C#、VB 和 F#; but others, such as IronPython, IronRuby, Delphi Prism etc are also relevant.但其他的,如 IronPython、IronRuby、Delphi Prism 等也与此相关。 The more of a corner case it is, the more interesting it will be - things like removing members are pretty self-evident, but subtle interactions between eg method overloading, optional/default parameters, lambda type inference, and conversion operators can be very surprising at times.极端情况越多,它就越有趣——像删除成员这样的事情是不言而喻的,但是方法重载、可选/默认参数、lambda 类型推断和转换运算符之间的微妙交互可能会非常令人惊讶有时。

A few examples to kickstart this:启动此操作的几个示例:

Adding new method overloads添加新的方法重载

Kind: source-level break种类:源级中断

Languages affected: C#, VB, F#受影响的语言:C#、VB、F#

API before change:更改前的API:

public class Foo
{
    public void Bar(IEnumerable x);
}

API after change:更改后的API:

public class Foo
{
    public void Bar(IEnumerable x);
    public void Bar(ICloneable x);
}

Sample client code working before change and broken after it:示例客户端代码在更改前工作并在更改后损坏:

new Foo().Bar(new int[0]);

Adding new implicit conversion operator overloads添加新的隐式转换运算符重载

Kind: source-level break.种类:源级中断。

Languages affected: C#, VB受影响的语言:C#、VB

Languages not affected: F#不受影响的语言:F#

API before change:更改前的API:

public class Foo
{
    public static implicit operator int ();
}

API after change:更改后的API:

public class Foo
{
    public static implicit operator int ();
    public static implicit operator float ();
}

Sample client code working before change and broken after it:示例客户端代码在更改前工作并在更改后损坏:

void Bar(int x);
void Bar(float x);
Bar(new Foo());

Notes: F# is not broken, because it does not have any language level support for overloaded operators, neither explicit nor implicit - both have to be called directly as op_Explicit and op_Implicit methods.注意:F# 没有损坏,因为它没有对重载运算符的任何语言级别支持,既不是显式也不是隐式 - 两者都必须作为op_Explicitop_Implicit方法直接调用。

Adding new instance methods添加新的实例方法

Kind: source-level quiet semantics change. Kind:源级安静语义变化。

Languages affected: C#, VB受影响的语言:C#、VB

Languages not affected: F#不受影响的语言:F#

API before change:更改前的API:

public class Foo
{
}

API after change:更改后的API:

public class Foo
{
    public void Bar();
}

Sample client code that suffers a quiet semantics change:遭受安静语义更改的示例客户端代码:

public static class FooExtensions
{
    public void Bar(this Foo foo);
}

new Foo().Bar();

Notes: F# is not broken, because it does not have language level support for ExtensionMethodAttribute , and requires CLS extension methods to be called as static methods.注意:F# 没有损坏,因为它没有对ExtensionMethodAttribute语言级别支持,并且需要将 CLS 扩展方法作为静态方法调用。


解决方案:

参考一: https://en.stackoom.com/question/66yX
参考二: https://stackoom.com/question/66yX
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章