Qual è la differenza tra “x is null” e “x == null”?


277

In C # 7 possiamo usare

if (x is null) return;

invece di

if (x == null) return;

Ci sono dei vantaggi nell'usare il nuovo modo (esempio precedente) rispetto al vecchio modo?

La semantica è diversa?

È solo una questione di gusti? In caso contrario, quando dovrei usarne uno rispetto all'altro?

Riferimento: Novità di C # 7.0 .


4
questo è il link che stavo solo guardando, tuttavia non ti dà molte informazioni ed è per questo che immagino che l'OP stia facendo la domanda. La parte più importante della pagina è che questo test è Operatore L'operatore "is" viene utilizzato per verificare se il tipo di runtime di un oggetto è compatibile con un determinato tipo o meno. In altre parole, utilizziamo l'operatore "is" per verificare che il tipo di un oggetto sia quello che ci aspettiamo che sia. Diamo un'occhiata alla sua sintassi:
Simon Price,

2
@SimonPrice Riguarda la versione corrente di C #: C # 6. Questa domanda riguarda C # 7, che ha una corrispondenza dei pattern .
Patrick Hofman,

@bigown che tipo di dettagli stai cercando?
Patrick Hofman,

@PatrickHofman il tipo di svick rispose, per esempio
Maniero,

Risposte:


232

Aggiornamento: il compilatore Roslyn è stato aggiornato per rendere lo stesso comportamento dei due operatori in assenza di un operatore di uguaglianza sovraccarico . Si prega di vedere il codice nei risultati del compilatore corrente ( M1e M2nel codice) che mostra cosa succede quando non esiste un comparatore di uguaglianza sovraccarico. Entrambi hanno ora il ==comportamento più performante . Se esiste un comparatore di uguaglianza sovraccarico, il codice differisce comunque .

Vedere per le versioni precedenti del compilatore Roslyn l'analisi di seguito.


Perché nullnon c'è differenza con ciò a cui siamo abituati con C # 6. Tuttavia, le cose diventano interessanti quando si nullpassa a un'altra costante.

Prendi questo per esempio:

Test(1);

public void Test(object o)
{
    if (o is 1) Console.WriteLine("a");
    else Console.WriteLine("b");
}

Il test cede a. Se lo paragoni a o == (object)1quello che avresti scritto normalmente, farà davvero la differenza. isprende in considerazione il tipo dall'altra parte del confronto. È fantastico!

Penso che il modello == nullvs. is nullcostante sia qualcosa che è molto familiare "per caso", in cui la sintassi isdell'operatore e dell'operatore uguale producono lo stesso risultato.


Come ha commentato svick , is nullchiama System.Object::Equals(object, object)dove ==chiamaceq .

IL per 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 per ==:

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

Dal momento che stiamo parlando null, non vi è alcuna differenza poiché ciò fa la differenza solo sulle istanze . Questo potrebbe cambiare quando hai sovraccaricato l'operatore di uguaglianza.


16
@PatrickHofman Sembra ischiamate object.Equals(x, null), mentre ==compila come ceq. Ma il risultato dovrebbe essere lo stesso, come hai detto.
svick,

17
Attenzione sempre che ==è un operatore sovraccaricabile. Puoi avere qualsiasi comportamento tu voglia con esso. Ad esempio, questa strana implementazione== non ti dirà se la tua istanza è veramente nulla. is nulld'altra parte tornerà sempre vero per veri riferimenti null :) Inoltre, se hai ReferenceEqualsnel tuo codice, le lampadine VS 2017 suggeriranno di cambiare in is null, non == null(correttamente).
nawfal,

2
@PatrickHofman @svick i due controlli null ora vengono compilati nella stessa cosa, quindi isnon ha più il sovraccarico di una chiamata di funzione quando viene utilizzato per verificare la presenza di null. Per la prova, vedi il link pubblicato da @svick nei commenti.
AndreasHassing l'

1
@ AndreasBjørnHassingNielsen Aggiornato la mia risposta.
Patrick Hofman,

2
@PatrickHofman non dovrebbe essere IL al contrario? == chiama System.Object :: Equals (oggetto, oggetto) ed è null chiama ceq
Zbigniew Ledwoń

68

Operatore uguale sovraccaricato

Vi è infatti una differenza di semantica tra i due confronti quando si confronta nullcon un tipo che ha sovraccaricato l' ==operatore. foo is nullutilizzerà il confronto diretto di riferimento per determinare il risultato, mentre foo == nullovviamente eseguirà l' ==operatore sovraccarico se esiste.

In questo esempio ho introdotto un "bug" nell'operatore sovraccarico ==, provocando sempre un'eccezione se il secondo argomento è 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 codice IL per foo is nullutilizza l' ceqistruzione per eseguire un confronto di riferimento diretto:

IL_0003:  ldloc.0     // foo
IL_0004:  ldnull      
IL_0005:  ceq

Il codice IL per foo == nullutilizza una chiamata all'operatore sovraccarico:

IL_0016:  ldloc.0     // foo
IL_0017:  ldnull      
IL_0018:  call        UserQuery+Foo.op_Equality

Quindi la differenza è che se si utilizza ==si rischia di eseguire codice utente (che può potenzialmente avere comportamenti imprevisti o problemi di prestazioni).

Restrizione ai generici

L'uso del is nullcostrutto limita il tipo a un tipo di riferimento. Il compilatore garantisce questo, il che significa che non è possibile utilizzare is nullsu un tipo di valore. Se si dispone di un metodo generico, non sarà possibile utilizzare a is nullmeno che il tipo generico non sia vincolato a un tipo di riferimento.

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

Grazie a David Augusto Villa per averlo segnalato.


2
Inoltre, la nota (x è null) richiede un vincolo di classe se x è un tipo generico, mentre (x == null) e object.ReferenceEquals (x, null) no.
David Augusto Villa,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.