Перевантажений дорівнює оператору
Насправді є різниця в семантиці між двома порівняннями, коли ви порівнюєте nullз типом, який перевантажив ==оператора. foo is nullбуде використовувати пряме порівняння порівняння для визначення результату, тоді як foo == null, звичайно, буде запущений перевантажений ==оператор, якщо він існує.
У цьому прикладі я ввів "помилку" в перевантажений ==оператор, внаслідок чого він завжди кидає виняток, якщо другий аргумент 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);
}
// ...
}
Іл-код для 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
Дякуємо Давидові Аугусто Віллі за те, що вказав на це.