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

查看:20
本文介绍了.NET 中 API 突破性更改的权威指南的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想收集尽可能多的关于 .NET/CLR 中 API 版本控制的信息,特别是 API 更改如何破坏或不破坏客户端应用程序.首先,让我们定义一些术语:

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. First, let's define some terms:

API 更改 - 类型的公开可见定义的更改,包括其任何公共成员.这包括更改类型和成员名称、更改类型的基类型、从类型的已实现接口列表中添加/删除接口、添加/删除成员(包括重载)、更改成员可见性、重命名方法和类型参数、添加默认值对于方法参数,添加/删除类型和成员的属性,以及添加/删除类型和成员的泛型类型参数(我错过了什么吗?).这不包括成员团体的任何变化,或私人成员的任何变化(即我们不考虑 Reflection).

API change - a change in the publicly visible definition of a type, including any of its public members. 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 (i.e. we do not take into account Reflection).

二进制级中断 - 一种 A​​PI 更改,导致针对旧版本 API 编译的客户端程序集可能不会随新版本一起加载.示例:更改方法签名,即使它允许以与以前相同的方式调用(即:void 返回类型/参数默认值重载).

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. 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).

源代码级中断 - 一种 A​​PI 更改,导致编写的现有代码针对旧版本的 API 进行编译,可能无法使用新版本进行编译.然而,已经编译的客户端程序集像以前一样工作.示例:添加新的重载可能会导致之前明确的方法调用出现歧义.

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. 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.

源级安静语义变化 - 一种 A​​PI 变化,导致现有代码编写以针对旧版本的 API 进行编译,悄悄地改变其语义,例如通过调用不同的方法.然而,代码应该继续编译而不会出现警告/错误,并且以前编译的程序集应该像以前一样工作.示例:在现有类上实现新接口,导致在重载解析期间选择不同的重载.

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, e.g. by calling a different method. 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.

最终目标是对尽可能多的破坏性和安静语义 API 更改进行分类,并描述破坏的确切影响,以及哪些语言受到和不受影响.扩展后者:虽然一些更改普遍影响所有语言(例如,向接口添加新成员将破坏该接口在任何语言中的实现),但有些更改需要非常特定的语言语义才能发挥作用才能中断.这通常涉及方法重载,并且通常涉及与隐式类型转换有关的任何事情.即使对于符合 CLS 的语言(即那些至少符合 CLI 规范中定义的CLS 消费者"规则的语言),似乎也没有任何方法可以在这里定义最小公分母"——尽管我会很感激有人纠正我在这里是错误的 - 所以这将不得不逐个语言地进行.最感兴趣的自然是 .NET 自带的那些:C#、VB 和 F#;但其他的,如 IronPython、IronRuby、Delphi Prism 等也与此相关.它的极端情况越多,它就越有趣——像删除成员这样的事情是不言而喻的,但是例如之间的微妙交互.方法重载、可选/默认参数、lambda 类型推断和转换运算符有时会非常令人惊讶.

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. To expand on the latter: while some changes affect all languages universally (e.g. 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 (i.e. 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. Those of most interest are naturally the ones that come with .NET out of the box: C#, VB and F#; but others, such as IronPython, IronRuby, Delphi Prism etc are also relevant. 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 e.g. method overloading, optional/default parameters, lambda type inference, and conversion operators can be very surprising at times.

启动此操作的几个示例:

A few examples to kickstart this:

种类:源码级中断

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

Languages affected: C#, VB, F#

变更前的API:

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

更改后的 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.

受影响的语言:C#、VB

Languages affected: C#, VB

不受影响的语言:F#

变更前的API:

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

更改后的 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());

注意:F# 没有被破坏,因为它没有任何语言级别的对重载运算符的支持,既不是显式也不是隐式 - 两者都必须直接调用为 op_Explicitop_Implicit方法.

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.

Kind:源级安静语义变化.

Kind: source-level quiet semantics change.

受影响的语言:C#、VB

Languages affected: C#, VB

不受影响的语言:F#

变更前的API:

public class Foo
{
}

变更后的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();

注意:F# 没有损坏,因为它没有对 ExtensionMethodAttribute 的语言级别支持,并且需要将 CLS 扩展方法作为静态方法调用.

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.

推荐答案

更改方法签名

种类:二进制级中断

Changing a method signature

Kind: Binary-level Break

受影响的语言:C#(最有可能是 VB 和 F#,但未经测试)

Languages affected: C# (VB and F# most likely, but untested)

变更前的API

public static class Foo
{
    public static void bar(int i);
}

变更后的API

public static class Foo
{
    public static bool bar(int i);
}

更改前工作的示例客户端代码

Sample client code working before change

Foo.bar(13);

这篇关于.NET 中 API 突破性更改的权威指南的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆