Operador igual sobrecarregado
De fato, há uma diferença na semântica entre as duas comparações quando você está comparando null
com um tipo que sobrecarregou o ==
operador. foo is null
usará comparação direta de referência para determinar o resultado, enquanto, foo == null
é claro, executará o ==
operador sobrecarregado , se ele existir.
Neste exemplo, introduzi um "bug" no ==
operador sobrecarregado , fazendo com que ele sempre lança uma exceção se o segundo argumento for 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);
}
// ...
}
O código IL para foo is null
usa a ceq
instrução para executar uma comparação direta de referência:
IL_0003: ldloc.0 // foo
IL_0004: ldnull
IL_0005: ceq
O código IL para foo == null
usa uma chamada para o operador sobrecarregado:
IL_0016: ldloc.0 // foo
IL_0017: ldnull
IL_0018: call UserQuery+Foo.op_Equality
Portanto, a diferença é que, se você usar, ==
corre o risco de executar o código do usuário (que pode potencialmente ter problemas inesperados de comportamento ou desempenho).
Restrição de genéricos
O uso da is null
construção restringe o tipo a um tipo de referência. O compilador garante isso, o que significa que você não pode usar is null
em um tipo de valor. Se você tiver um método genérico, não poderá usá-lo is null
, a menos que o tipo genérico seja restrito a ser um tipo de referência.
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
Agradecemos a David Augusto Villa por apontar isso.