3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

.NET 10のTimeSpan.FromMillisecondsメソッドのオーバーロードの追加について

Posted at

.NET 10で、TimeSpan型のFromMillisecondsメソッドに新しいオーバーロードが追加されます。

.NET 10で追加されるFromMillisecondsのオーバーロード

.NET 9時点で、TimeSpan型のFromMillisecondsメソッドは次の2つのオーバーロードがあります。

public static TimeSpan FromMilliseconds(double value);
public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0);

2つ目のオーバーロードは、.NET 9で加わった比較的新しいオーバーロードです。第2引数としてoptional引数なmicrosecondsという引数をとります。

さて、.NET 10でさらに新たなオーバーロードが追加されます。追加されるオーバーロードは、long型の引数millisecondsのみを取ります。また、既存のlong型を2つ引数にとるオーバーロードは、optional引数でなく通常の引数を取るようになります。

public static TimeSpan FromMilliseconds(double value);
public static TimeSpan FromMilliseconds(long milliseconds);
public static TimeSpan FromMilliseconds(long milliseconds, long microseconds);

追加された理由

.NET 9で追加されたこのオーバーロード、一見良さそうなんですが実は問題がありました。

public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0);

次に示すような、式ツリーを作るコードでコンパイルエラーになってしまいます。

Expression<Action> a = () => TimeSpan.FromMilliseconds(1000);

コンパイルエラーのメッセージは次のとおりです。

「An expression tree cannot contain a call or invocation that uses optional arguments」

式ツリーにおいて、optional引数は使えないようです。そのため、.NET 10で次の様なオーバーロードが追加され、

public static TimeSpan FromMilliseconds(long milliseconds);

次のオーバーロードのoptional引数が無くなりました。

public static TimeSpan FromMilliseconds(long milliseconds, long microseconds);

この追加により、.NET 10では次に示すコードがコンパイルできるようになりました。

Expression<Action> a = () => TimeSpan.FromMilliseconds(1000);

破壊的な変更ではない?

「この変更、破壊的な変更じゃないと思うけれど、もしかして破壊的な変更じゃないか?」と思って調べてみました。


まず「.NET 10の破壊的な変更一覧」に、記載がないかを確認しました。こちらには、記載されていませんでした。


.NET ライブラリの変更に関する規則には、次の様な記載がありました。

❌未許可: プロパティ、フィールド、またはパラメーターの既定値を変更する

パラメーターの既定値の変更または削除は、バイナリ区切りではありません。 パラメーターの既定値を削除すると、ソースの中断が発生し、パラメーターの既定値を変更すると、再コンパイル後に動作が中断する可能性があります。

このため、あいまいさをなくすために、既定値を新しいメソッド オーバーロードに "移動" する特定のケースでは、パラメーターの既定値を削除することができます。 たとえば、既存のメソッド MyMethod(int a = 1) を考えてみます。 2 つの省略可能なパラメーター a と b を使用して MyMethod のオーバーロードを導入する場合は、a の既定値を新しいオーバーロードに移動することで互換性を維持できます。 ここで、2 つのオーバーロードは MyMethod(int a) と MyMethod(int a = 1, int b = 2) です。 このパターンでは、MyMethod() をコンパイルできます。

このため、あいまいさをなくすために、既定値を新しいメソッド オーバーロードに "移動" する特定のケースでは、パラメーターの既定値を削除することができます。

今回のFromMillisecondsメソッドのオーバーロードの追加は、optional引数(上記ドキュメントでは規定値)を削除し、オーバーロードに移動するケースですね。


念の為、コード変更が互換性に影響を与えるしくみを確認します。

まずは、「ソースの互換性」。

ソースの互換性とは、API の既存のコンシューマーが、ソースを変更せずに新しいバージョンに再コンパイルできることを指します。 "ソース非互換な変更" は、コンシューマーが新しいバージョンの API が正しくビルドされるように、ソース コードを変更する必要がある場合に発生します。

次に示すような、TimeSpan型のFromMillisecondsメソッドを呼び出しているソースコードがあるとします。

var timespan = TimeSpan.FromMilliseconds(500);

.NET 9と.NET 10の間で、呼び出されるオーバーロードは変わりますが、正しく再コンパイルできるため、ソースの互換性は保てていそうです。


次は、「バイナリの互換性」。

バイナリの互換性とは、API のコンシューマーが、再コンパイルせずに新しいバージョンで API を使用できることです。 型へのメソッドの追加または新しいインターフェイス実装の追加などの変更は、バイナリの互換性には影響しません。 しかし、アセンブリによって公開されるインターフェイスと同じものにコンシューマーがアクセスできなくなるように、アセンブリのパブリック シグネチャが削除または変更されると、バイナリの互換性が影響を受けます。 このような種類の変更は、"バイナリ非互換な変更" と呼ばれます。

次に示すオーバーロードを使って.NET 9でコンパイルされたバイナリは、.NET 10で変更されたAPIを、再コンパイルせずとも使用できそうです。

public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0);

確認していきます。

まず、次に示すように第2引数を明示して呼び出しているソースコードを、.NET 9でコンパイルしたバイナリがあるとします。.NET 10でも、変わらず引数を2つ取るオーバーロードを呼び出しており、問題なく使用することができます。

var timespan0 = TimeSpan.FromMilliseconds(500, 0);
var timespan1 = TimeSpan.FromMilliseconds(500, 100);

そして、次に示すoptional引数を活用しているコードを、.NET 9でコンパイルしたバイナリがあるとします。

var timespan3 = TimeSpan.FromMilliseconds(500);

optional引数は、メソッド利用側(呼び出し側)のバイナリに、optional引数の値が埋め込まれます。そのため、.NET 9でコンパイルしたバイナリは実質、次に示すような呼び出しとなっています。

var timespan3 = TimeSpan.FromMilliseconds(500, 0);

そのため、NET 10でも変わらず引数を2つ取るオーバーロードを呼び出しており、こちらも再コンパイルする必要なく、問題なく使用することができます。


次に、「動作の変更」です。

動作の変更とは、メンバーの動作の変更を指します。 この変更は、外部から参照可能 (たとえば、メソッドが別の例外をスローするなど) である場合があります。または、変更された実装 (たとえば、戻り値の計算方法の変更、メソッドの内部呼び出しの追加または削除、またはパフォーマンスの大幅な向上など) を指す場合があります。

動作の変更が、外部から参照可能、かつ型のパブリック コントラクトを変更するものである場合、バイナリの互換性に影響するため、簡単に評価できます。 実装の変更の評価はさらに困難です。変更の性質、および API の使用の頻度およびパターンによって、変更の影響は深刻なものから無害なものにまで及びます。

これは、変更PRを確認すると、実装の変更はされています。しかし、破壊的な変更ではなさそうです。

まとめ

本投稿では、.NETにおけるoptional引数関連の互換性に触れつつ、.NET 10で追加されるTimeSpan型のFromMillisecondsメソッドの新しいオーバーロードを紹介しました。

関連リンク

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?