Por que é &&
preferível &
e ||
preferível |
?
Perguntei a alguém que programa há anos e sua explicação foi:
Por exemplo, em if (bool1 && bool2 && bool3) { /*DoSomething*/ }
, bool1
deve ser verdadeiro para testar o bool2
que deve ser verdadeiro antes de passar para bool3
etc. Se eu tivesse usado um único, &
não há ordem para o teste, mesmo que todos eles tenham que ser verdadeiros. progredir para a próxima linha, então por que isso importa?
Nota: gostaria de salientar que sou o equivalente de programação de uma criança pequena e essa não é uma pergunta séria ou urgente. É mais uma questão de entender por que as coisas devem ser feitas de uma certa maneira e não de outra.
x & y
ex && y
sempre avaliarão o mesmo resultado se xey forem expressões do tipo booleano. De fato, a única diferença nesse caso parece ser aquela emx & y
y sempre é avaliada.Respostas:
Na maioria dos casos,
&&
e||
são preferidos aos demais&
e|
porque os primeiros estão em curto-circuito, o que significa que a avaliação é cancelada assim que o resultado é claro.Exemplo:
Se
CanExecute
retornarfalse
, a expressão completa seráfalse
, independentemente do valor de retorno deCanSave
. Por isso,CanSave
não é executado.Isso é muito útil nas seguintes circunstâncias:
TryGetValue
retornafalse
se a chave fornecida não for encontrada no dicionário. Devido à natureza de curto-circuito de&&
,value.Contains("test")
só é executado quandoTryGetValue
retornatrue
e, portanto,value
não énull
. Se você usasse o operador AND bit a bit&
, obteria umNullReferenceException
se a chave não for encontrada no dicionário, porque a segunda parte da expressão é executada em qualquer caso.Um exemplo semelhante, porém mais simples, é o seguinte código (conforme mencionado por TJHeuvel):
CanExecute
só é executado seop
não estivernull
. Seop
fornull
, a primeira parte da expressão (op != null
) é avaliadafalse
e a avaliação do resto (op.CanExecute()
) é ignorada.Além disso, tecnicamente, eles são diferentes, também:
&&
e||
só pode ser usado embool
enquanto&
e|
pode ser usado em qualquer tipo integral (bool
,int
,long
,sbyte
, ...), porque eles são operadores bit a bit.&
é o operador AND bit a bit e|
é o operador OR bit a bit .Para ser muito exato, em C #, esses operadores (
&
,|
[e^
]) são chamados de "Operadores lógicos" (consulte a especificação de C # , capítulo 7.11). Existem várias implementações desses operadores:int
,uint
,long
eulong
, no capítulo 7.11.1):Eles são implementados para calcular o resultado bit a bit dos operandos e o operador, ou seja,
&
é implementar para calcular a lógica bit a bitAND
etc.Elas são implementadas para executar a operação lógica do tipo subjacente da enumeração.
O resultado não é calculado usando cálculos bit a bit. O resultado é basicamente pesquisado com base nos valores dos dois operandos, porque o número de possibilidades é muito pequeno.
Como os dois valores são usados para a pesquisa, essa implementação não está em curto-circuito.
fonte
if(op != null && op.CanExecute())
. Como a segunda causa não é avaliada quando a primeira não é verdadeira, isso é válido.TryGetValue
exemplo. Mas sim, é outro bom exemplo disso.&
ou|
é usado com argumentos não-booleanos (isto é, o que os operadores fazem) para o benefício de todas as pessoas novas.Para explicar muito claramente o que isso significa (mesmo que as outras respostas indiquem isso - mas provavelmente use terminologia que você não entende).
O código a seguir:
É realmente compilado para isso:
Onde o código a seguir é compilado exatamente como está representado:
Isso é chamado de curto-circuito. Em geral, você deve sempre usar
&&
e||
em suas condições.Marcas de bônus: Há um cenário em que você não deveria. Se você estiver em uma situação onde o desempenho é crucial (e este é nano-segundos cruciais ) usar apenas um curto-circuito quando você deve (por exemplo,
null
controlo) - como um curto-circuito é um ramo / salto; o que pode resultar em erro de ramificação na sua CPU; um&
é muito mais barato que&&
. Há também um cenário em que o curto-circuito pode realmente quebrar a lógica - dê uma olhada nesta minha resposta .Diatribe / Monólogo : Quanto ao ramo de mis-previsão de que mais alegremente ignorar. Citando Andy Firth (que trabalha com jogos há 13 anos): "Esse pode ser um nível mais baixo que as pessoas pensam que precisam seguir ... mas elas estariam erradas. Entender como o hardware que você está programando para tratar ramos pode afeta o desempenho em um nível ENORME ... muito mais do que a maioria dos programadores pode apreciar a re-morte por mil cortes. "
Aqui está uma referência para os não-crentes. É melhor executar o processo no RealTime / High para reduzir o efeito do agendador: https://gist.github.com/1200737
fonte
(x && y)
traduz paraLOAD x; BRANCH_FALSE; LOAD y; BRANCH_FALSE;
onde(x & y)
traduzLOAD x; LOAD y; AND; BRANCH_FALSE;
. Um ramo versus dois.Operador lógico (
||
e&&
) vs. operador bit a bit (|
e&
).A diferença mais crucial entre um operador lógico e um operador bit a bit é que um operador lógico pega dois booleanos e produz um booleano, enquanto um operador bit a bit pega dois números inteiros e produz um número inteiro (nota: inteiros significa qualquer tipo de dados integral, não apenas int).
Para ser pedante, um operador bit a bit usa um padrão de bits (por exemplo, 01101011) e executa AND / OR em cada bit. Portanto, por exemplo, se você tiver dois números inteiros de 8 bits:
enquanto um operador lógico trabalha apenas em
bool
:Segundo, muitas vezes é possível usar um operador bit a bit no bool, pois true e false são equivalentes a 1 e 0, respectivamente, e acontece que se você traduzir true para 1 e false para 0, faça a operação bit a bit e converta diferente de zero para verdadeiro e zero para falso; acontece que o resultado será o mesmo se você tivesse acabado de usar o operador lógico (verifique isso no exercício).
Outra distinção importante é também que um operador lógico está em curto-circuito . Assim, em alguns círculos [1], você costuma ver pessoas fazendo algo assim:
que se traduz em: "se a pessoa existe (ou seja, não é nula), tente dar um soco nela e, se o soco for bem-sucedido (ou seja, retornar verdadeiro), faça uma dança da vitória" .
Se você tivesse usado um operador bit a bit, isso:
irá traduzir para: "se a pessoa existe (ou seja, não é nula) e o soco é bem-sucedido (ou seja, retorna verdadeiro), faça uma dança da vitória" .
Observe que no operador lógico em curto-circuito, o
person.punch()
código pode não ser executado seperson
for nulo. De fato, nesse caso específico, o segundo código produziria um erro de referência nulo seperson
for nulo, pois ele tenta chamarperson.punch()
, independentemente de a pessoa ser nula ou não. Esse comportamento de não avaliar o operando certo é chamado de curto-circuito .[1] Alguns programadores se recusam a colocar uma chamada de função que tenha um efeito colateral dentro de uma
if
expressão, enquanto para outros é um idioma comum e muito útil.Como um operador bit a bit trabalha em 32 bits por vez (se você estiver em uma máquina de 32 bits), pode levar a um código mais elegante e mais rápido se você precisar comparar um grande número de condições, por exemplo,
Fazer o mesmo com operadores lógicos exigiria uma quantidade incômoda de comparações:
Um exemplo clássico em que padrões de bits e operador bit a bit são usados está nas permissões do sistema de arquivos Unix / Linux.
fonte
No caso de:
funcionaria como esperado.
Mas:
potencialmente poderia lançar uma exceção de referência nula.
fonte
Curto e simples:
1 && 2
= verdadeiroporque
1 = verdadeiro (diferente de zero) em C
2 = verdadeiro (diferente de zero) em C
true
ANDS logicamente comtrue
para dartrue
.Mas
1 & 2
= 0 = falsoporque
1 = 0001 no binário
2 = 0010 no binário
0001 ANDs bit a bit com 0010 para fornecer 0000 = 0 em decimal.
O mesmo para || e | operadores também ...!
fonte
1 && 2
é ilegal em c # #&&
é a versão de curto-circuito do&
.Se estamos avaliando
false & true
, já sabemos, olhando para o primeiro argumento, que o resultado será falso. A&&
versão do operador retornará um resultado o mais rápido possível, em vez de avaliar toda a expressão. Há também uma versão semelhante do|
operador||
,.fonte
é seguro
falharia se a lista não tivesse o tamanho certo.
fonte
Operadores C # devem explicar o porquê:
Essencialmente, ter dois
&
ou dois|
significa que é mais um condicional do que um lógico, para que você possa ver a diferença entre os dois.& Operador tem um exemplo de uso de um
&
.fonte
OK, no valor nominal
produz a mesma resposta. No entanto, como você mostrou, se você tiver uma pergunta mais complexa, faça o seguinte:
Se
a
não é verdade e talvezb
seja uma função em que ela deve ocorrer , conecte-se a algo, faça isso, faça aquilo, tome uma decisão .. por que se preocupar? Perda de tempo, você sabe que já falhou. Por que fazer a máquina desligar e fazer um trabalho extra inútil?Eu sempre usei
&&
porque coloco o mais provável de falhar primeiro, logo, menos cálculos antes de prosseguir quando não há sentido. Se não houver maneira de prever escolhas menos prováveis, como você tem um booleano para limitar a saída de dados, algo como:Caso contrário
limit
, não se preocupe em verificar a chave, o que pode levar mais tempo.fonte
Quando usado em uma expressão lógica, como uma instrução if, é
&&
preferível, pois interromperá a avaliação de expressões assim que o primeiro resultado falso for encontrado. Isso é possível porque um valor falso fará com que toda a expressão seja falsa. Da mesma forma (e novamente em expressões lógicas)||
é preferível, pois interromperá a avaliação de expressões assim que encontrar uma expressão verdadeira, pois qualquer valor verdadeiro fará com que toda a expressão seja verdadeira.Se, no entanto, as expressões sendo editadas ou editadas juntas tiverem efeitos colaterais e você desejar que tudo isso ocorra como resultado de sua expressão (independentemente do resultado da expressão lógica), então
&
e|
poderá ser usado. Por outro lado, o&&
e||
operadores podem ser úteis como proteção contra efeitos colaterais indesejados (como um ponteiro nulo, causando a exceção).Os operadores
&
e|
também podem ser usados com números inteiros e, nesse caso, eles produzem um resultado inteiro que são os dois operandos e-ed ou-ed juntos no nível de bit. Isso pode ser útil quando os bits binários de um valor inteiro são usados como uma matriz de valores verdadeiros e falsos. Para testar se um determinado bit está ativado ou desativado, uma máscara de bit é bit a bit e ed com o valor Para ativar um pouco, a mesma máscara pode ser dividida em bits ou com o valor. Finalmente, para desativar um pouco, o complemento bit a bit (usando~
) de uma máscara é bit a bit e ed com o valorEm idiomas diferentes de C #, é necessário ter cuidado com os modos lógico versus bit a bit de & e |. No código acima, a
if
expressão condicional da instrução(a & 4) != 0
é uma maneira segura de expressar essa condição, mas em muitos idiomas C, as instruções condicionais podem simplesmente tratar zero valores inteiros como falsos e valores inteiros diferentes de zero como verdadeiros. (O motivo disso está relacionado às instruções condicionais do processador de ramificação disponíveis e a relação delas com o sinalizador zero que é atualizado após cada operação inteira.) Portanto, oìf
teste da instrução para zero pode ser removido e a condição pode ser reduzida para(a & 4)
.Isso pode causar confusão e talvez até problemas quando expressões combinadas usando o operador bit a bit e o operador retornam valores que não possuem bits alinhados. Considere o seguinte exemplo em que os efeitos colaterais de duas funções são desejados, antes de verificar se ambos foram bem-sucedidos (conforme definido por eles retornando um valor diferente de zero):
Em C, se
foo()
retornar 1 ebar()
retornar 2, o "algo" não será feito porque1 & 2
é zero.O C # requer instruções condicionais, como
if
ter uma oeprand booleana, e o idioma não permite que um valor inteiro seja convertido em um valor booleano. Portanto, o código acima geraria erros do compilador. Seria mais corretamente expresso da seguinte maneira:fonte
Se você é um programador C antigo, tenha cuidado . C # realmente me surpreendeu.
O MSDN diz para o
|
operador:(A ênfase é minha.) Os tipos booleanos são manipulados especialmente e, nesse contexto, a pergunta começa a fazer sentido, e a diferença é que outros já foram esperados em suas respostas:
e o que é preferível depende de vários fatores, como efeitos colaterais, desempenho e legibilidade do código, mas geralmente os operadores de curto-circuito também são preferíveis porque são mais bem compreendidos por pessoas com experiência semelhante à minha.
O motivo é: eu argumentaria assim: Como não existe um tipo booleano real em C, você pode usar o operador bit a bit
|
e ter seu resultado avaliado como verdadeiro ou falso em uma condição if. Mas essa é a atitude errada para C #, porque já existe um caso especial para tipos booleanos.fonte
É importante, porque se o custo da avaliação do bool2 (por exemplo) for alto, mas o bool1 for falso, você economizou um pouco de computação usando && over &
fonte
Porque
&&
e||
são usados para controle de fluxo comoif/else
são. Nem sempre é sobre condicionais. É perfeitamente razoável escrever como uma declaração, não comoif
ouwhile
condicional, o seguinte:ou mesmo
Não é apenas o fato de serem mais fáceis de digitar do que as
if/else
versões equivalentes ; eles também são muito mais fáceis de ler e entender.fonte
&& e & significam duas coisas muito diferentes e oferecem duas respostas diferentes.
1 && 2
produz 1 ("verdadeiro")1 & 2
gera 0 ("falso")&&
é um operador lógico - significa "verdadeiro se ambos os operandos forem verdadeiros"&
é uma comparação bit a bit. Significa "me diga qual dos bits está definido nos dois operandos"fonte
1 && 2
dá um erro de compilador: "O erro 4 do operador '&&' não pode ser aplicado a operandos do tipo 'int' e 'int'"A maneira mais rápida (e um pouco confusa) de explicar isso para as pessoas que NÃO PRECISAM conhecer as operações exatas do código ao fazer isso é
&& está verificando cada uma dessas condições até encontrar um falso e retornar o resultado inteiro como falso
|| está verificando cada uma dessas condições até encontrar um verdadeiro e retornar o resultado inteiro como verdadeiro.
& está fazendo MATHS com APONTA / TODAS as condições e lidando com o resultado.
| está fazendo MATHS baseado em ambas / todas as condições e lidando com o resultado.
Nunca me deparei com um ponto em que precisei usar & ou | dentro de uma instrução if. Eu o uso principalmente para cortar valores hexadecimais em suas cores de componentes usando deslocamento bit a bit.
POR EXEMPLO:
Dentro desta operação "& 0xFF" está forçando a olhar apenas para o valor binário. Eu pessoalmente não encontrei um uso para | ainda assim.
fonte
Simplesmente,
if exp1 && exp2
se exp1 for
flase
, não verifique exp2mas
if exp1 & exp2
se exp1 for
false
Outrue
verifique exp2e raramente as pessoas usam
&
porque raramente querem verificar exp2 se exp1 forfalse
fonte