Por que costumamos ver “null! = Variable” em vez de “variable! = Null” em C #?

103

Em c #, há alguma diferença na velocidade de execução para a ordem em que você declara a condição?

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

Recentemente, vi o primeiro com bastante frequência, e isso me chamou a atenção porque estava acostumado com o segundo.

Se não houver diferença, qual a vantagem do primeiro?

mr_georg
fonte
Algumas linguagens de programação oferecem suporte para inicializar o valor na condição if. Em tais linguagens, se quisermos comparar LValue com RValue, é uma boa prática usar literal para o lado esquerdo como if (1 == i). então, acidentalmente, se colocarmos = em vez ==, ocorrerá um erro de compilador.
Jugal Panchal

Respostas:

160

É um resquício de C. Em C, se você usar um compilador ruim ou não tiver avisos altos o suficiente, isso será compilado sem nenhum aviso (e é de fato um código legal):

// Probably wrong
if (x = 5)

quando você provavelmente quis dizer

if (x == 5)

Você pode contornar isso em C fazendo:

if (5 == x)

Um erro de digitação aqui resultará em código inválido.

Agora, em C #, isso é tudo tolice. A menos que você esteja comparando dois valores booleanos (o que é raro, IME), você pode escrever o código mais legível, pois uma instrução "if" requer uma expressão booleana para começar, e o tipo de " x=5" Int32não é Boolean.

Eu sugiro que, se você vir isso no código de seus colegas, você os eduque nas formas das linguagens modernas e sugira que escrevam a forma mais natural no futuro.

Jon Skeet
fonte
6
Todos os meus colegas me deixam maluco com isso, e querendo colchetes mesmo para uma única instrução após o if ... (no caso de você adicionar algo mais tarde 'sem perceber'). Role F # e Boo (e YAML), se seu idioma for ilegível sem indentação, torne-o parte do idioma, eu digo.
Benjol
48
Adicionar colchetes é razoável, IMO-C # certamente não faz qualquer tentativa de impedir que seja um problema. Isso é muito diferente do problema da "constante == variável".
Jon Skeet
6
a if (this! = that) {performAsSuch (); } é bastante saudável, na verdade. O argumento de "adição posterior" parece-me tão frágil quanto não evitar / detectar se (a = 5) erros de digitação
jpinto3912
3
@jpinto: Eu não estou realmente certo o que você está tentando dizer aqui - que você faz como chaves em torno de declarações individuais, ou que você não faz? A diferença entre isso e o negócio variável em questão é que em C # o código com um erro de digitação é inválido , então o compilador irá interrompê-lo. O mesmo não é verdade para não colocar chaves.
Jon Skeet
3
@Benjol Não se esqueça de sempre fornecer uma else { }cláusula vazia caso alguém precise adicionar algo ali mais tarde e se esqueça de escrever a elsepalavra-chave. (Piada)
Jeppe Stig Nielsen
12

Há um bom motivo para usar nulo primeiro: if(null == myDuck)

Se você class Ducksubstitui o ==operador, if(myDuck == null)pode entrar em um loop infinito.

Usar nullprimeiro usa um comparador de igualdade padrão e realmente faz o que você pretendia.

(Ouvi dizer que você se acostuma a ler código escrito dessa maneira - só não experimentei essa transformação ainda).

Aqui está um exemplo:

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
    }
}
DanW
fonte
11
Isto está errado. Votos negativos. Basta apontar o mouse sobre o ==operador e colocá-lo ali, e você verá que a sobrecarga definida pelo usuário é usada em ambos os casos. Claro que pode fazer uma diferença sutil se você verificar aantes be depois trocar a posição do aoperando da direita para o operando da esquerda. Mas você também poderia verificar bantes a, e então b == nullseria aquele que inverteu a ordem. Isso tudo é confuso para que a operadora ligue para si mesma. Em vez disso, converta qualquer operando (ou ambos) em objectpara obter a sobrecarga desejada. Gosto if ((object)a == null)ou if (a == (object)null).
Jeppe Stig Nielsen
10

Como todo mundo já notou, ele vem mais ou menos da linguagem C, onde você pode obter um código falso se acidentalmente esquecer o segundo sinal de igual. Mas há outro motivo que também corresponde ao C #: legibilidade.

Veja este exemplo simples:

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

Se você simplesmente trocar todas as palavras nulas para o início, poderá identificar com muito mais facilidade todas as verificações:

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

Portanto, este exemplo pode ser um mau exemplo (consulte as diretrizes de codificação), mas pense em rolar rapidamente por um arquivo de código completo. Simplesmente vendo o padrão

if(null ...

você sabe imediatamente o que está por vir.

Se fosse o contrário, você sempre teria que escanear até o final da linha para ver o cheque de nulidade, apenas deixando você cambalear por um segundo para descobrir que tipo de cheque é feito lá. Então, talvez o realce de sintaxe possa ajudá-lo, mas você sempre é mais lento quando essas palavras-chave estão no final da linha em vez de na frente.

Oliver
fonte
9

Eu acho que este é um programador C que mudou de idioma.

Em C, você pode escrever o seguinte:

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

Observe o uso de um único sinal de igual aqui, o que significa que o código atribuirá 1 à variável i, em seguida, retornará 1 (uma atribuição é uma expressão) e usará 1 na instrução if, que será tratada como verdadeira. Em outras palavras, o acima é um bug.

Em C #, entretanto, isso não é possível. Na verdade, não há diferença entre os dois.

Lasse V. Karlsen
fonte
1
"Em C # entretanto, isso não é possível" sem a reclamação do compilador, já que a instrução if requer uma expressão booleana e a fornecida é do tipo int.
Robert Harvey,
4

Antigamente, as pessoas esqueceriam o '!' (ou o extra '=' para igualdade, que é mais difícil de detectar) e faça uma atribuição em vez de uma comparação. colocar o nulo na frente elimina a possibilidade do bug, já que nulo não é um valor l (ou seja, ele não pode ser atribuído).

A maioria dos compiladores modernos dá um aviso quando você faz uma atribuição em uma condicional hoje em dia, e o C # na verdade fornece um erro. A maioria das pessoas apenas segue o esquema var == null, pois é mais fácil de ler para algumas pessoas.

Rik
fonte
Todos os compiladores C # (observe que esta é uma questão C #) devem falhar ao compilar uma instrução "if" em que a expressão não seja um booleano, embora ...
Jon Skeet
4

Não vejo nenhuma vantagem em seguir esta convenção. Em C, onde não existem tipos booleanos, é útil escrever

if( 5 == variable)

ao invés de

if (variable == 5)

porque se você esquecer um dos sinais de equal, você acaba com

if (variable = 5)

que atribui 5 à variável e sempre avalia como verdadeiro. Mas em Java, um booleano é um booleano. E com! =, Não há razão alguma.

Um bom conselho, porém, é escrever

if (CONSTANT.equals(myString))

ao invés de

if (myString.equals(CONSTANT))

porque ajuda a evitar NullPointerExceptions.

Meu conselho seria pedir uma justificativa para a regra. Se não há nenhum, por que segui-lo? Não ajuda na legibilidade

Gokkula Sudan R
fonte
0

Para mim sempre foi o estilo que você prefere

@Shy - Então, novamente, se você confundir os operadores, deverá obter um erro de compilação ou estará executando o código com um bug - um bug que volta e o morde mais tarde, pois produziu um comportamento inesperado

TheCodeJunkie
fonte
Eu não acho que eu já ouvi falar de alguém preferindo a "constante == variável" form outro do que por razões de segurança, as quais já são tratadas no C #.
Jon Skeet
Eu mesmo prefiro "variável == constante", mas já vi programadores adotarem o inverso porque acham que é mais natural para eles.
TheCodeJunkie
1
Eles eram todos pessoas com anos de lavagem cerebral C atrás deles, ou foi uma escolha genuína?
Jon Skeet
0

Como muitos apontaram, é principalmente em código C antigo que era usado para identificar erros de compilação, já que o compilador o aceitava como legal

Novas linguagens de programação como java, go são inteligentes o suficiente para capturar tais erros de compilação

Não se deve usar "null! = Variable" como condições no código, pois é muito ilegível

Anand Shah
fonte
-4

Mais uma coisa ... Se você estiver comparando uma variável a uma constante (inteiro ou string, por exemplo), colocar a constante à esquerda é uma boa prática porque você nunca encontrará NullPointerExceptions:

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

enquanto que

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

Agora, como por padrão o C # instancia todas as variáveis, você não deve ter esse problema nessa linguagem.

Karlipoppins
fonte
1
Não tenho certeza de qual compilador você está usando para o primeiro bit de código, mas ele deve compilar (geralmente com um aviso). O valor de i é indefinido, mas tem ALGUM valor.
Dolphin
Claro que os dois códigos serão compilados! Esse é exatamente o problema. Tente executar o primeiro código e você entenderá o que quero dizer com "Exceção levantada" ...
karlipoppins
é também não perceber a diferença entre os dois. Você pode elaborar?
mfazekas
Sem exceção, a menos que você declare int * i em C / C ++ e, mesmo nesse caso, ele comparará os ponteiros e não lançará nenhuma exceção ... Como o Dolphin afirmou, o valor é indefinido, mas não gerará nenhuma exceção.
Cobusve
sem exceções, digamos que iseja um valor aleatório sem uma inicialização adequada. a expressão tem completamente a mesma semântica em c / c ++
Raffaello