C#7では、
if (x is null) return;
の代わりに
if (x == null) return;
古い方法よりも新しい方法(以前の例)を使用する利点はありますか?
セマンティクスは異なりますか?
好みの問題ですか?そうでない場合、いつどちらを使用すればよいですか?
参考:C#7.0の新機能。
C#7では、
if (x is null) return;
の代わりに
if (x == null) return;
古い方法よりも新しい方法(以前の例)を使用する利点はありますか?
セマンティクスは異なりますか?
好みの問題ですか?そうでない場合、いつどちらを使用すればよいですか?
参考:C#7.0の新機能。
回答:
更新: Roslynコンパイラーが更新され、オーバーロードされた等価演算子がない場合に 2つの演算子の動作が同じになるようになりました。オーバーロードされた等値比較子がない場合に何が起こるかを示す現在のコンパイラーの結果(M1
およびM2
コード)のコードを参照してください。どちらもパフォーマンスが向上してい==
ます。オーバーロードされた等価比較子がある場合でも、コードは異なります。
古いバージョンのRoslynコンパイラについては、以下の分析を参照してください。
ためにnull
、我々はC#で使っているものとの違いがないあなたが変更されたとき6.しかし、物事は面白いとなりnull
、別の定数に。
これを例にとります:
Test(1);
public void Test(object o)
{
if (o is 1) Console.WriteLine("a");
else Console.WriteLine("b");
}
テストの結果はa
です。それをo == (object)1
あなたが普通に書いたであろうものと比較すると、それは違いの地獄を作ります。is
比較の反対側のタイプを考慮します。それはクールだ!
== null
vs. is null
定数パターンは、「偶然」によく知られているものであり、is
演算子と等号演算子の構文は同じ結果をもたらすと思います。
以下のようsvickはコメントし、is null
呼び出しをSystem.Object::Equals(object, object)
どこ==
通話ceq
。
のIL is
:
IL_0000: ldarg.1 // Load argument 1 onto the stack
IL_0001: ldnull // Push a null reference on the stack
IL_0002: call bool [mscorlib]System.Object::Equals(object, object) // Call method indicated on the stack with arguments
IL_0007: ret // Return from method, possibly with a value
のIL ==
:
IL_0000: ldarg.1 // Load argument 1 onto the stack
IL_0001: ldnull // Push a null reference on the stack
IL_0002: ceq // Push 1 (of type int32) if value1 equals value2, else push 0
IL_0004: ret // Return from method, possibly with a value
私たちが話しているのでnull
、これはインスタンスに違いをもたらすだけなので違いはありません。これは、等価演算子をオーバーロードしたときに変更される可能性があります。
is
呼び出しのように見えます。object.Equals(x, null)
==
ceq
しかし、あなたが言ったように、結果は同じになるはずです。
is
なったため、nullのチェックに使用したときに関数呼び出しのオーバーヘッドがなくなりました。証明については、コメントに@svickが投稿したリンクを参照してください。
演算子null
をオーバーロードした型と比較する場合、実際には2つの比較のセマンティクスに違いがあります==
。foo is null
直接参照比較を使用して結果を決定しfoo == null
ますが、オーバーロードされた==
演算子が存在する場合はもちろん実行します
この例では、オーバーロードされた==
演算子に「バグ」を導入しました。これにより、2番目の引数が次の場合に常に例外がスローされますnull
。
void Main()
{
Foo foo = null;
if (foo is null) Console.WriteLine("foo is null"); // This condition is met
if (foo == null) Console.WriteLine("foo == null"); // This will throw an exception
}
public class Foo
{
public static bool operator ==(Foo foo1, Foo foo2)
{
if (object.Equals(foo2, null)) throw new Exception("oops");
return object.Equals(foo1, foo2);
}
// ...
}
のILコードfoo is null
は、ceq
命令を使用して直接参照比較を実行します。
IL_0003: ldloc.0 // foo
IL_0004: ldnull
IL_0005: ceq
のILコードはfoo == null
、オーバーロードされたオペレーターへの呼び出しを使用します。
IL_0016: ldloc.0 // foo
IL_0017: ldnull
IL_0018: call UserQuery+Foo.op_Equality
したがって、違いは、使用する==
とユーザーコードを実行するリスクがあることです(予期しない動作やパフォーマンスの問題が発生する可能性があります)。
構成を使用するとis null
、タイプが参照タイプに制限されます。コンパイラはこれを保証します。つまりis null
、値型では使用できません。ジェネリックメソッドがあるis null
場合、ジェネリックタイプが参照タイプに制約されていない限り、使用できません。
bool IsNull<T>(T item) => item is null; // Compile error: CS0403
bool IsNull<T>(T item) => item == null; // Works
bool IsNull<T>(T item) where T : class => item is null; // Works
これを指摘してくれたDavid Augusto Villaに感謝します。