.NET / CLRでのAPIのバージョン管理、特にAPIの変更がクライアントアプリケーションに影響する、または影響しない方法に関する情報をできるだけ多く収集したいと考えています。まず、いくつかの用語を定義しましょう:
APIの変更 -パブリックメンバーを含む、タイプの公に見える定義の変更。これには、タイプとメンバー名の変更、タイプの基本タイプの変更、タイプの実装済みインターフェースのリストからのインターフェースの追加/削除、メンバーの追加/削除(オーバーロードを含む)、メンバーの可視性の変更、メソッドとタイプパラメーターの名前変更、デフォルト値の追加が含まれますメソッドパラメーター、型とメンバーの属性の追加/削除、および型とメンバーのジェネリック型パラメーターの追加/削除(何か見落としましたか?)これには、メンバー組織の変更やプライベートメンバーの変更は含まれません(つまり、リフレクションは考慮されません)。
バイナリレベルのブレーク -APIの変更により、古いバージョンのAPIに対してコンパイルされたクライアントアセンブリが、新しいバージョンで読み込まれない可能性があります。例:メソッドシグネチャを変更します。以前と同じ方法で呼び出すことができます(例:voidでタイプ/パラメータのデフォルト値のオーバーロードを返す)。
ソースレベルのブレーク -APIの変更により、古いバージョンのAPIに対してコンパイルするように記述された既存のコードが、新しいバージョンでコンパイルされない可能性があります。ただし、コンパイル済みのクライアントアセンブリは以前と同様に機能します。例:以前はあいまいでなかったメソッド呼び出しにあいまいさをもたらす可能性がある新しいオーバーロードを追加します。
ソースレベルの静かなセマンティクスの変更-APIの変更により、古いバージョンのAPIに対してコンパイルするように作成された既存のコードは、たとえば別のメソッドを呼び出すことによって、そのセマンティクスを静かに変更します。ただし、コードは警告/エラーなしでコンパイルを続行し、以前にコンパイルされたアセンブリは以前と同様に機能するはずです。例:既存のクラスに新しいインターフェースを実装すると、オーバーロードの解決時に別のオーバーロードが選択されます。
最終的な目標は、可能な限り多くの破壊的で静かなセマンティクスAPIの変更をカタログ化し、破壊の正確な影響と、影響を受ける言語と影響を受けない言語を説明することです。後者について詳しく説明します。一部の変更はすべての言語に普遍的に影響します(たとえば、新しいメンバーをインターフェイスに追加すると、そのインターフェイスの実装が任意の言語で機能しなくなります)。これには、最も一般的にはメソッドのオーバーロードが含まれ、一般に、暗黙的な型変換に関係するすべての処理が含まれます。CLS準拠の言語(つまり、少なくともCLI仕様で定義されている「CLSコンシューマー」のルールに準拠している言語)であっても、「最小公分母」を定義する方法はないようです。ここで誰かが私を間違っていると訂正してくれれば感謝します-したがって、これは言語ごとに行われる必要があります。最も興味深いのは、当然、.NETに付属しているもので、C#、VB、F#です。しかし、IronPython、IronRuby、Delphi Prismなどのその他のものも関連します。それがコーナーケースであるほど、それはより興味深いものになります-メンバーの削除などのことはかなり自明ですが、たとえばメソッドのオーバーロード、オプション/デフォルトのパラメーター、ラムダ型の推論、および変換演算子の間の微妙な相互作用は非常に驚くことができます時には。
これをキックスタートするいくつかの例:
新しいメソッドオーバーロードの追加
種類:ソースレベルの区切り
影響を受ける言語: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);
}
変更前に機能し、その後壊れたサンプルクライアントコード:
new Foo().Bar(new int[0]);
新しい暗黙の変換演算子のオーバーロードを追加する
種類:ソースレベルの区切り。
影響を受ける言語: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 ();
}
変更前に機能し、その後壊れたサンプルクライアントコード:
void Bar(int x);
void Bar(float x);
Bar(new Foo());
注:F#は壊れていません。F#はオーバーロードされた演算子に対する言語レベルのサポートがなく、明示的でも暗黙的でもないためです。どちらもメソッドop_Explicit
とop_Implicit
メソッドとして直接呼び出す必要があります。
新しいインスタンスメソッドの追加
種類:ソースレベルの静かなセマンティクスが変更されました。
影響を受ける言語:C#、VB
影響を受けない言語:F#
変更前のAPI:
public class Foo
{
}
変更後のAPI:
public class Foo
{
public void Bar();
}
静かなセマンティクスの変更を受けるサンプルクライアントコード:
public static class FooExtensions
{
public void Bar(this Foo foo);
}
new Foo().Bar();
注:F#ExtensionMethodAttribute
はの言語レベルのサポートがないため、壊れていません。また、CLS拡張メソッドを静的メソッドとして呼び出す必要があります。