.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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章