Perché spesso si vede "null! = Variable" invece di "variable! = Null" in C #?


103

In c #, c'è qualche differenza nella velocità di esecuzione per l'ordine in cui dichiari la condizione?

if (null != variable) ...
if (variable != null) ...

Da poco, ho visto il primo abbastanza spesso, e ha attirato la mia attenzione da quando ero abituato al secondo.

Se non c'è differenza, qual è il vantaggio del primo?



Alcuni linguaggi di programmazione supportano l'inizializzazione del valore nella condizione if. In tali linguaggi, se vogliamo confrontare LValue con RValue, è buona norma usare il letterale a sinistra come se (1 == i). così accidentalmente se mettiamo = invece == allora dà un errore del compilatore.
Jugal Panchal

Risposte:


160

È un residuo di C. In C, se usi un cattivo compilatore o non hai avvisi abbastanza alti, questo verrà compilato senza alcun avviso (ed è effettivamente codice legale):

// Probably wrong
if (x = 5)

quando in realtà probabilmente intendevi

if (x == 5)

Puoi aggirare questo problema in C facendo:

if (5 == x)

Un errore di battitura qui risulterà in un codice non valido.

Ora, in C # questo è tutto piffle. A meno che tu non stia confrontando due valori booleani (cosa rara, IME) puoi scrivere il codice più leggibile, poiché un'istruzione "if" richiede un'espressione booleana con cui iniziare e il tipo di " x=5" è Int32, not Boolean.

Suggerisco che se vedi questo nel codice dei tuoi colleghi, li istruisci nei modi dei linguaggi moderni e suggerisci loro di scrivere la forma più naturale in futuro.


6
Tutti i miei colleghi mi fanno impazzire con questo, e vogliono le parentesi graffe anche per una singola affermazione dopo il se ... (nel caso in cui si aggiunga qualcosa in seguito "senza rendersene conto"). Tira su F # e Boo (e YAML), se la tua lingua è illeggibile senza indentazione, rendila parte della lingua, dico.
Benjol

48
L'aggiunta di parentesi graffe è ragionevole, IMO - C # certamente non fa alcun tentativo per impedire che sia un problema. Questo è molto diverso dal problema "costante == variabile".
Jon Skeet

6
a if (this! = that) {performAsSuch (); } è abbastanza salutare in realtà. L'argomento di "aggiunta successiva" mi sembra fragile quanto non evitare / individuare se (a = 5) errori di battitura
jpinto3912

3
@jpinto: Io non sono davvero sicuro di quello che si sta cercando di dire qui - che si fa come le parentesi intorno singole dichiarazioni, o non fare? La differenza tra questo e la variabile business nella domanda è che in C # il codice con un errore di battitura è un codice non valido , quindi il compilatore lo interromperà. Lo stesso non è vero per non mettere le parentesi graffe.
Jon Skeet,

3
@ Benjol Non dimenticare di fornire sempre una else { }clausola vuota nel caso in cui qualcuno abbia bisogno di aggiungere qualcosa in seguito e dimentichi di scrivere la elseparola chiave da solo. (Scherzo)
Jeppe Stig Nielsen

12

C'è una buona ragione per usare prima null: if(null == myDuck)

Se class Ducksostituisci l' ==operatore, allora if(myDuck == null)puoi entrare in un ciclo infinito.

Il nullprimo utilizzo utilizza un comparatore di uguaglianza predefinito e in realtà fa ciò che intendevi.

(Ho sentito che alla fine ti abitui a leggere il codice scritto in quel modo - non ho ancora sperimentato quella trasformazione).

Ecco un esempio:

public class myDuck
{
    public int quacks;
    static override bool operator ==(myDuck a, myDuck b)
    {
        // these will overflow the stack - because the a==null reenters this function from the top again
        if (a == null && b == null)
            return true;
        if (a == null || b == null)
            return false;

        // these wont loop
        if (null == a && null == b)
            return true;
        if (null == a || null == b)
            return false;
        return a.quacks == b.quacks; // this goes to the integer comparison
    }
}

11
Questo è sbagliato. Downvoted. Basta puntare il mouse ==sull'operatore e appoggiarlo lì, e vedrai che il sovraccarico definito dall'utente viene utilizzato in entrambi i casi. Ovviamente può fare una sottile differenza se controlli aprima be poi scambia la posizione di ada operando destro a operando sinistro. Ma potresti anche controllare bprima a, e poi b == nullsarebbe quello che ha invertito l'ordine. Tutto ciò crea confusione quando l'operatore chiama se stesso. Eseguire invece il cast di uno degli operandi (o di entrambi) objectper ottenere l'overload desiderato. Come if ((object)a == null)o if (a == (object)null).
Jeppe Stig Nielsen

10

Come tutti hanno già notato, deriva più o meno dal linguaggio C dove potresti ottenere un codice falso se dimentichi accidentalmente il secondo segno di uguale. Ma c'è un altro motivo che corrisponde anche a C #: leggibilità.

Prendi questo semplice esempio:

if(someVariableThatShouldBeChecked != null
   && anotherOne != null
   && justAnotherCheckThatIsNeededForTestingNullity != null
   && allTheseChecksAreReallyBoring != null
   && thereSeemsToBeADesignFlawIfSoManyChecksAreNeeded != null)
{
    // ToDo: Everything is checked, do something...
}

Se semplicemente scambiassi tutte le parole nulle all'inizio, puoi individuare molto più facilmente tutti i controlli:

if(null != someVariableThatShouldBeChecked
   && null != anotherOne
   && null != justAnotherCheckThatIsNeededForTestingNullity
   && null != allTheseChecksAreReallyBoring
   && null != thereSeemsToBeADesignFlawIfSoManyChecksAreNeeded)
{
    // ToDo: Everything is checked, do something...
}

Quindi questo esempio è forse un cattivo esempio (fare riferimento alle linee guida per la codifica) ma pensa solo a scorrere rapidamente un file di codice completo. Semplicemente vedendo lo schema

if(null ...

sai subito cosa succederà dopo.

Se fosse il contrario, devi sempre scansionare fino alla fine della riga per vedere l'assegno di nullità, lasciandoti inciampare per un secondo per scoprire che tipo di assegno viene effettuato lì. Quindi forse l'evidenziazione della sintassi può aiutarti, ma sei sempre più lento quando quelle parole chiave sono alla fine della riga anziché in primo piano.


9

Immagino che questo sia un programmatore C che ha cambiato lingua.

In C, puoi scrivere quanto segue:

int i = 0;
if (i = 1)
{
    ...
}

Notare l'uso di un singolo segno di uguale lì, il che significa che il codice assegnerà 1 alla variabile i, quindi restituirà 1 (un'assegnazione è un'espressione) e utilizzerà 1 nell'istruzione if, che verrà gestita come vera. In altre parole, quanto sopra è un bug.

In C #, tuttavia, ciò non è possibile. Non c'è davvero alcuna differenza tra i due.


1
"In C #, tuttavia, questo non è possibile" senza che il compilatore si lamenti, poiché l'istruzione if richiede un'espressione booleana e quella fornita è di tipo int.
Robert Harvey,

4

In passato, la gente dimenticava il "!" (o il '=' extra per l'uguaglianza, che è più difficile da individuare) e fai un compito invece di un confronto. mettere il null davanti elimina la possibilità del bug, poiché null non è un valore l (IE non può essere assegnato a).

La maggior parte dei compilatori moderni fornisce un avviso quando si esegue un'assegnazione in un condizionale al giorno d'oggi, e C # in realtà dà un errore. La maggior parte delle persone si limita a seguire lo schema var == null poiché è più facile da leggere per alcune persone.


Tutti i compilatori C # (nota che questa è una domanda C #) non dovrebbero compilare un'istruzione "if" in cui l'espressione non è booleana però ...
Jon Skeet

4

Non vedo alcun vantaggio nel seguire questa convenzione. In C, dove i tipi booleani non esistono, è utile scrivere

if( 5 == variable)

piuttosto che

if (variable == 5)

perché se dimentichi uno dei segni eaqual, finisci con

if (variable = 5)

che assegna 5 alla variabile e restituisce sempre vero. Ma in Java, un booleano è un booleano. E con! =, Non c'è alcun motivo.

Un buon consiglio, però, è scrivere

if (CONSTANT.equals(myString))

piuttosto che

if (myString.equals(CONSTANT))

perché aiuta a evitare NullPointerExceptions.

Il mio consiglio sarebbe di chiedere una giustificazione della regola. Se non ce n'è, perché seguirlo? Non aiuta la leggibilità


0

Per me è sempre stato lo stile che preferisci

@Shy - Poi di nuovo se confondi gli operatori, dovresti voler ottenere un errore di compilazione o eseguirai il codice con un bug - un bug che si ripresenta e ti morde più tardi lungo la strada poiché ha prodotto un comportamento inaspettato


Non credo di aver mai sentito parlare di qualcuno che preferisca la forma "costante == variabile" se non per motivi di sicurezza, già trattati in C #.
Jon Skeet

Io stesso preferisco "variabile == costante", ma ho visto programmatori adottare l'inverso perché ritengono che sia più naturale per loro.
TheCodeJunkie

1
Erano tutte persone con anni di lavaggio del cervello C alle spalle o è stata una scelta genuina?
Jon Skeet

0

Come molti hanno sottolineato, è principalmente nel vecchio codice C che è stato utilizzato per identificare l'errore di compilazione, poiché il compilatore lo ha accettato come legale

Il nuovo linguaggio di programmazione come java, go è abbastanza intelligente da catturare tali errori di compilazione

Non si dovrebbe usare "null! = Variable" come condizioni nel codice in quanto è molto illeggibile


-4

Un'altra cosa ... Se stai confrontando una variabile con una costante (numero intero o stringa per es.), Mettere la costante a sinistra è una buona pratica perché non ti imbatterai mai in NullPointerExceptions:

int i;
if(i==1){        // Exception raised: i is not initialized. (C/C++)
  doThis();
}

mentre

int i;
if(1==i){        // OK, but the condition is not met.
  doThis();
}

Ora, poiché per impostazione predefinita C # installa tutte le variabili, non dovresti avere quel problema in quella lingua.


1
Non sono sicuro di quale compilatore stai usando per il primo bit di codice, ma dovrebbe essere compilato (generalmente con un avviso). Il valore di i non è definito, ma ha QUALCHE valore.
Dolphin

Ovviamente entrambi i codici verranno compilati! Questo è esattamente il problema. Prova a eseguire il primo codice e capirai cosa intendo per "eccezione sollevata" ...
karlipoppins

è anche non capire la differenza tra i due. Puoi elaborare?
mfazekas

Nessuna eccezione a meno che tu non dichiari int * i in C / C ++, e anche in quel caso confronterà i puntatori e non genererà alcuna eccezione ... Come ha affermato Dolphin, il valore non è definito ma non genererà alcuna eccezione.
Cobusve

nessuna eccezione, diciamo che iè un valore casuale senza una corretta inizializzazione. l'espressione ha completamente la stessa semantica in c / c ++
Raffaello
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.