??
C#の演算子は、評価時に短絡を使用しますか?
var result = myObject ?? ExpressionWithSideEffects();
myObject
が null でない場合、 is の結果は使用ExpressionWithSideEffects()
されませんが、ExpressionWithSideEffects()
完全にスキップされますか?
??
C#の演算子は、評価時に短絡を使用しますか?
var result = myObject ?? ExpressionWithSideEffects();
myObject
が null でない場合、 is の結果は使用ExpressionWithSideEffects()
されませんが、ExpressionWithSideEffects()
完全にスキップされますか?
はい、短絡します。
LinqPad でテストするスニペットを次に示します。
string bar = "lol";
string foo = bar ?? string.Format("{2}", 1);
foo.Dump();
bar = null;
foo = bar ?? string.Format("{2}", 1);
foo.Dump();
最初の結合は例外をスローせずに機能しますが、2 番目の結合は例外をスローします (フォーマット文字列が無効です)。
はい、そうです。相変わらず、C# 言語仕様は決定的なソースです1。
C# 3 仕様のセクション 7.12 から (v4 仕様はここではあまり関係のない動的な詳細になるため、4 ではなく v3 です):
式の型
a ?? b
は、オペランドの型間で使用できる暗黙的な変換によって異なります。?? のタイプは優先順で b は A0、A、または B です。ここで、A は a の型、B は b の型 (b に型がある場合)、A が null 許容型の場合は A0 が A の基になる型、それ以外の場合は A です。 . 具体的にa ?? b
は、次のように処理されます。
- A が null 許容型または参照型でない場合、コンパイル時エラーが発生します。
- A が null 許容型で、b から A0 への暗黙的な変換が存在する場合、結果の型は A0 になります。実行時に、 a が最初に評価されます。a が null でない場合、a は型 A0 にアンラップされ、これが結果になります。それ以外の場合、b が評価され、型 A0 に変換され、これが結果になります。
- それ以外の場合、b から A への暗黙的な変換が存在する場合、結果の型は A になります。実行時に、a が最初に評価されます。a が null でない場合、a が結果になります。それ以外の場合、b が評価されて型 A に変換され、これが結果になります。
- それ以外の場合、b の型が B で、A0 から B への暗黙的な変換が存在する場合、結果の型は B になります。実行時に、a が最初に評価されます。a が null でない場合、a は A0 型にラップ解除され (A と A0 が同じ型でない場合)、B 型に変換され、これが結果になります。それ以外の場合、b が評価され、結果になります。
- それ以外の場合、a と b は互換性がなく、コンパイル時エラーが発生します。
2 番目、3 番目、4 番目の箇条書きが該当します。
1たまたま使用しているコンパイラが実際の真実の情報源であるかどうかについて、哲学的な議論が必要です...言語についての真実は、それが意図していることなのか、それとも現在何をしているのか?
これが、単体テストがある理由です。
[TestMethod]
public void ShortCircuitNullCoalesceTest()
{
const string foo = "foo";
var result = foo ?? Bar();
Assert.AreEqual(result, foo);
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShortCircuitNullCoalesceFails()
{
const string foo = null;
var result = foo ?? Bar();
}
private static string Bar()
{
throw new ArgumentException("Bar was called");
}
これらは最適なテスト名ではありませんが、アイデアは理解できます。null 合体演算子が期待どおりに短絡していることを示しています。