+3 votos
124 visitas

Compartilho com todos a dúvida de um membro interno.

No if por exemplo, qual a diferença entre o operador | e || ? Tem diferença?

fechada com o comentário: Mais informações no Stackoverflow: http://stackoverflow.com/questions/35301/what-is-the-difference-between-the-and-or-operators.
por (623 pontos)
fechada por | 124 visitas

2 Respostas

+8 votos
Melhor resposta

Bem, os operadores | e & são classificados como lógicos e para uso em bitwise (manipulação de Bits). Os operadores || e && são classificados como condicionais e exclusivos para lógica boolean, porém de uma forma mais eficiente (dependendo da necessidade) do que os operadores lógicos.

Diferença entre & e && , | e ||

Usando como exemplo uma condição com duas expressões:

  • O | (OU lógico) ou o & (E lógico) verificará as duas expressões booleanas para obter o resultado final da expressão.
  • O || (OU relacional) verificará a primeira expressão, se for verdadeira não há a necessidade de verificar a segunda.
  • O && (E relacional) só verificará a segunda expressão se a primeira for verdadeira. Se a primeira for falsa, nem precisa verificar a segunda pois o resultado final é falso.

​Esse comportamento dos operadores || e && é conhecida como Avaliação de Curto Circuito (short-circuit evaluation).

Na prática

Tomem como exemplo o código fonte abaixo:

using System.Diagnostics;

public static void Principal()
{
    Debug.WriteLine("IF ||:");

    if (MetodoTrue() || MetodoFalse())
        Debug.WriteLine("Entrou no IF ||");

    Debug.WriteLine("IF |:");
    if (MetodoTrue() | MetodoFalse())
        Debug.WriteLine("Entrou no IF |");

    Debug.WriteLine("IF &&:");

    if (MetodoFalse() && MetodoTrue())
        Debug.WriteLine("Entrou no IF &&");
    else
        Debug.WriteLine("Entrou no Else IF &&");

    Debug.WriteLine("IF &:");
    if (MetodoFalse() & MetodoTrue())
        Debug.WriteLine("Entrou no IF &");
    else           
        Debug.WriteLine("Entrou no Else IF &");
}

public static bool MetodoTrue()
{
    Debug.WriteLine("Entrou no MetodoTrue");
    return true;
}

public static bool MetodoFalse()
{
    Debug.WriteLine("Entrou no MetodoFalse");
    return false;
}

Vejam a saída da execução do método principal:

IF ||:
Entrou no MetodoTrue
Entrou no IF ||
IF |:
Entrou no MetodoTrue
Entrou no MetodoFalse
Entrou no IF |
IF &&:
Entrou no MetodoFalse
Entrou no Else IF &&
IF &:
Entrou no MetodoFalse
Entrou no MetodoTrue
Entrou no Else IF &

Vejam como os IFs com | e & tiveram que acessar os dois métodos para poder ter acesso ao resultado final. Se utilizamos || e && a verificação passa a ser mais eficiente, pois não precisará verificar todos os pontos.

por (623 pontos)
selecionada por
Importante!

Bem, com base nisso, temos outro aprendizado importante.

Se os operadores de Curto Circuito não precisam fazer todas as verificações para a tomada de decisão, devemos colocar sempre como primeiras condições do “IF/While...” as mais leve, que terão menos processamento, acesso a dados...

Observe:

Solução 1:

if ((ViagemDocumento.ExistsByDocumentoLogistica(this.Handle, considerarCancelados: false)) &&

    (this.TipoLotacao.Handle == eTipoLotacao.Fracionado.Index))

{

...

}

Na solução 1, mesmo que o TipoLotacao seja diferente de Fracionado o sistema faria o acesso ao banco de dados para verificar a existência de documentos (ExistsByDocumentoLogistica).

Digamos, que o sistema gaste:

10ms para  (this.TipoLotacao.Handle == eTipoLotacao.Fracionado.Index))

200ms ((ViagemDocumento.ExistsByDocumentoLogistica(this.Handle, considerarCancelados: false)

A simples troca de ordem, manteria a expressão válida, porém o sistema passa realizar o acesso ao banco apenas quando a primeira expressão for verdadeira, ou seja, TipoLotacao igual a Fracionado.

Quando o TipoLotacao for Lotação, nem precisa realizar o acesso ao banco, pois a primeira expressão não é válida.

Solução 2:

if ((this.TipoLotacao.Handle == eTipoLotacao.Fracionado.Index) &&

    (ViagemDocumento.ExistsByDocumentoLogistica(this.Handle, considerarCancelados: false)))

{

....

}

Na solução 1, o sistema sempre gastaria seus 200ms para verificação da primeira condição, e em alguns casos +10ms para a segunda condição

Já na solução 2, o sistema gastaria 10ms para verificação da primeira condição, e em alguns casos +200ms.

Se consideramos que 50% dos documentos é fracionado e os outros 50% lotação processando 1000 documentos teríamos:

Tempo por Documento:
   

                               Solução 1    Solução 2
Doc Fracionado      210               210
Doc Lotação           200                 10

Tempo para os 1000 documentos:

                                         Solução 1        Solução 2
500 Docs Fracionado        105.000            105.000
500 Docs Lotação             100.000                5.000
Total 1000 Docs:               205.000             110.000

Ou seja, se executássemos essa expressão 1000 vezes dentro das condições acima, a solução 1 levaria 205 segundos e a solução 2 levaria 110 segundos.

A solução 2 executaria praticamente na metade do tempo da solução 1.

Essas simples trocas e avaliações sobre o que deve ser verificado primeiro pode trazer ganhos significativos de performance no sistema.

Para Saber Mais:

http://msdn.microsoft.com/en-us/library/6a71f45d.aspx
Importante também sempre observar qual impacto a não execução de uma das expressões implicará para a lógica em questão (não somente a performance).

Eventualmente a expressão dispararia um método que faria falta para a continuidade do algoritmo.

Neste caso o programador poderia optar por forçar a execução de todas as expressões, *quando não for possível refatorar*, afinal é sempre bom evitar códigos exóticos, mas eles existem...
Interessante, porém em nome da legibilidade eu prefiro deixar o & e I apenas para bitwise/Flags.
+2 votos

Os operadores && e || ('e' e 'ou', respectivamente) são utilizados para combinar valores lógicos (boleanos) em expresões. Os if/else's que a gente conhece bem.


Já os operadores & e | ('e bitwise' e 'ou bitwise'), a utilidade que mais vejo por aí é para combinar valores de um enumerador que está com o atributo [Flags]. Com um exemplo fica melhor entender.


Considere que você tenha o seguinte enum:

   

    [Flags]
    public enum Permissao
    {
        SemPermissao = 0,
        Abrir = 1,
        Inserir = 2,
        Consultar = 4,
        Atualizar = 8,
        ListarDetalhes = 16
    }

Você pode usar este enum para testar os valores desta forma:

   

public class Sistema
    {
        public Permissao Pode { get; set; }
    }
 
    public void TestarPermissao()
    {
        var teste = new Sistema();

        if (teste.Pode == (Permissao.Abrir & Permissao.Consultar & Permissao.ListarDetalhes))
        {
            //Pode abrir E consultar E listar detalhes.
        }

        if (teste.Pode == (Permissao.Inserir | Permissao.Atualizar))
        {
            //Pode inserir OU atualizar.
        }

        //Você também pode definir valores desta forma. Por exemplo:
        teste.Pode = Permissao.Abrir & Permissao.ListarDetalhes; //Pode abrir E listar detalhes.

        //Ou:
        teste.Pode = Permissao.Abrir;
        teste.Pode &= Permissao.ListarDetalhes;
        //Pode abrir E listar detalhes.

    }

 

Mais informações: http://msdn.microsoft.com/en-us/library/system.flagsattribute.aspx

por (72 pontos)
editado por

Perguntas relacionadas

+6 votos
1 resposta
perguntado 16 Set, 2014 por bruno.cardoso (272 pontos) | 60 visitas
+5 votos
1 resposta
+1 voto
1 resposta
perguntado 4 Jan, 2019 por juliano.pezzini (539 pontos) | 40 visitas
Melhores May 2020
  1. henrique.muller

    18 Pontos

  2. joao.melo

    14 Pontos

  3. joseglauber

    11 Pontos

  4. SlimShady

    7 Pontos

  5. willian.metalsystem

    6 Pontos

  6. lucas.melo

    3 Pontos

  7. fluipress.luciano

    2 Pontos

  8. pajucara.wallacef

    2 Pontos

  9. jean.filho

    2 Pontos

  10. maicon.pereira

    2 Pontos

200 pontos
Melhores 2020 May 25 - 31
    433 perguntas
    476 respostas
    346 comentários
    466 usuários