c# tutorial Perché "&&" e non "&"?




double elvis c# (13)

Perché è && preferibile a & e || preferibile a | ?

Ho chiesto a qualcuno che stava programmando da anni e la sua spiegazione era:

Ad esempio, in if (bool1 && bool2 && bool3) { /*DoSomething*/ } , bool1 deve essere true per testare bool2 che deve essere vero prima di passare a bool3 , ecc. Se avessi usato un singolo & invece non c'è nessun ordine al test, anche se tutti devono essere veri per passare alla riga successiva, quindi perché è importante comunque?

Nota: vorrei far notare che sono l'equivalente di programmazione di un bambino piccolo e questa non è una domanda seria o urgente. È più una questione di capire perché le cose dovrebbero essere fatte in un certo modo piuttosto che in un altro.


Perché && e || sono usati per il controllo del flusso proprio come if/else . Non si tratta sempre di condizionali. È perfettamente ragionevole scrivere come affermazione, non come condizionale if o un while , come segue:

 a() && b() && c() && d();

o anche

 w() || x() || y() || z();

Non è solo che sono più facili da scrivere rispetto alle versioni equivalenti if/else ; sono anche molto più facili da leggere e capire.


È importante, perché se il costo della valutazione di bool2 (ad esempio) è alto ma bool1 è falso, allora hai risparmiato un bel po 'di calcolo usando && over &


Breve e semplice:

1 && 2 = vero
perché
1 = vero (diverso da zero) in C
2 = vero (diverso da zero) in C

true ANDS logicamente con true per dare true .

Ma

1 & 2 = 0 = falso
perché
1 = 0001 in binario
2 = 0010 in binario

0001 ANDs bit a bit con 0010 per dare 0000 = 0 in decimale.

Allo stesso modo per || e | anche gli operatori ...!


Gli operatori C # dovrebbero spiegare perché:

Essenzialmente avendo due & 's o | significa che è un condizionale piuttosto che un logico, quindi puoi dire la differenza tra i due.

& Operator ha un esempio di utilizzo di & .


Quando usato in un'espressione logica come un'istruzione if e preferibile perché smetterà di valutare le espressioni non appena viene rilevato il primo risultato falso. Questo è possibile perché un valore falso causerà la falsificazione dell'intera espressione. Allo stesso modo (e di nuovo nelle espressioni logiche) || è preferibile perché smetterà di valutare le espressioni non appena incontra un'espressione vera perché qualsiasi valore vero farà sì che l'intera espressione sia vera.

Se tuttavia le espressioni che sono o-ed o ed ed-insieme hanno effetti collaterali, e vuoi che tutte queste cose accadano come risultato della tua espressione (indipendentemente dal risultato dell'espressione logica), allora & e | potrebbe essere usato. Al contrario, il && e || gli operatori possono essere utili come protezioni contro gli effetti collaterali indesiderati (come un puntatore nullo che causa l'emissione di un'eccezione).

Il & e | gli operatori possono anche essere usati con interi e in questo caso producono un risultato intero che è i due operandi e-ed o o-ed insieme a livello di bit. Questo può essere utile quando i bit binari di un valore intero vengono usati come una matrice di valori veri e falsi. Per verificare se un certo bit è attivato o disattivato, una maschera bit è bit eded con il valore. Per attivare un bit, la stessa maschera può essere orientata verso il bit o il valore. Infine, per disattivare un po ', il complemento bit per bit (usando ~ ) di una maschera è bit ed editato con il valore.

int a = 0; // 0 means all bits off
a = a | 4; // set a to binary 100
if ((a & 4) != 0) {
    // will do something
}
a = a & (~4) // turn bit off again, a is now 000

Nelle lingue diverse da C #, è necessario prestare attenzione con i modi logico e bit per bit di & e | |. Nel codice sopra, l'espressione condizionale dell'istruzione if (a & 4) != 0 è un modo sicuro per esprimere questa condizione, ma in molte lingue simili a C, le istruzioni condizionali possono semplicemente trattare zero valori interi come valori interi falsi e diversi da zero come vero (La ragione di ciò si riferisce alle istruzioni del processore di ramo condizionale disponibili e alla loro relazione con il flag di zero che viene aggiornato dopo ogni operazione dell'intero.) Quindi il test ìf per zero può essere rimosso e la condizione potrebbe essere abbreviata in (a & 4) .

Ciò potrebbe causare confusione e forse anche problemi quando le espressioni combinate utilizzano i valori di bit e di ritorno dell'operatore che non hanno bit allineati. Considera il seguente esempio in cui sono desiderati gli effetti collaterali di due funzioni, prima di verificare che entrambi abbiano avuto esito positivo (come definito da essi restituendo un valore diverso da zero):

if (foo() & bar()) {
    // do something
}

In C, se foo() restituisce 1 e bar() restituisce 2, il "qualcosa" non verrà eseguito perché 1 & 2 è zero.

C # richiede istruzioni condizionali come if avesse un oeprand booleano e il linguaggio non consente il cast di un valore intero su un valore booleano. Quindi il codice sopra genererebbe errori del compilatore. Sarebbe più correttamente espresso come segue:

if (foo() != 0 & bar() != 0) {
    // do something
}

In caso di:

if (obj != null && obj.Property == true) { }

funzionerebbe come previsto

Ma:

if (obj != null & obj.Property == true) { }

potrebbe potenzialmente generare un'eccezione di riferimento null.


OK, sul valore nominale

    Boolean a = true;
    Boolean b = false;

    Console.WriteLine("a({0}) && b({1}) =  {2}", a, b, a && b);
    Console.WriteLine("a({0}) || b({1}) =  {2}", a, b, a || b);
    Console.WriteLine("a({0}) == b({1}) =  {2}", a, b, a == b);

    Console.WriteLine("a({0}) & b({1}) =  {2}", a, b, a & b);
    Console.WriteLine("a({0}) | b({1}) =  {2}", a, b, a | b);
    Console.WriteLine("a({0}) = b({1}) =  {2}", a, b, a = b);

produrre la stessa risposta. Tuttavia, come hai mostrato, se hai una domanda più complessa quindi:

if (a and b and c and d) ..

Se a non è vero e forse b è una funzione in cui deve andare fuori, connettersi a qualcosa, ottenere questo, farlo, prendere una decisione .. perché preoccuparsi? Spreco di tempo, sai che è già fallito. Perché far funzionare la macchina e fare lavori inutili extra?

Ho sempre usato && perché ho messo più probabilità di fallire prima, ergo, meno calcoli prima di andare avanti quando non c'è punto. Se non c'è modo di prevedere scelte meno probabili, come ad esempio un booleano per limitare l'output di dati, qualcosa come:

if (limit && !MyDictionary.ContainsKey("name")) 
    continue;

Se non è un limit , non preoccuparti di controllare la chiave, che potrebbe richiedere più tempo ..


Per spiegare molto chiaramente cosa significa (anche se l'altro risponde a questo - ma probabilmente usi la terminologia che non capisci).

Il seguente codice:

if (a && b)
{
   Foo();
}

È davvero compilato per questo:

if (a)
{
    if (b)
    {
        Foo();
    }
}

Dove il seguente codice è compilato esattamente come è rappresentato:

if (a & b)
{
   Foo();
}

Questo è chiamato cortocircuito. In generale dovresti sempre usare && e || nelle tue condizioni.

Punti bonus: c'è uno scenario in cui non si dovrebbe. Se ti trovi in ​​una situazione in cui la performance è cruciale (e questo è fondamentale per i nano secondi ) usa solo il cortocircuito quando devi (ad esempio il controllo null ) - poiché un cortocircuito è un salto / salto; che potrebbe causare una mancata previsione delle branch sulla tua CPU; un & è molto più economico di && . C'è anche uno scenario in cui il cortocircuito può effettivamente infrangere la logica: dai un'occhiata a questa mia risposta .

Diatriba / Monologo : Riguarda la falsa previsione del ramo che ignoriamo beatamente. Citando Andy Firth (che ha lavorato ai giochi per 13 anni): "Questo potrebbe essere il livello più basso che le persone pensano di dover andare ... ma si sbagliano. Capire come l'hardware che si sta programmando per trattare i rami può influire sulle prestazioni ad un livello ENORME ... molto più di quanto la maggior parte dei programmatori possa apprezzare: la morte di mille tagli ".

  • Gli sviluppatori di giochi (e altri che lavorano in condizioni estreme in tempo reale) arrivano fino a ristrutturare la loro logica per adattarsi meglio al predittore. Ci sono anche prove di questo nel codice mscorlib decompilato.
  • Solo perché .NET ti protegge da questo tipo di cose non significa che non sia importante. Una errata previsione di ramo è orribilmente costosa a 60 Hz; o a 10.000 richieste / secondo.
  • Intel non avrebbe strumenti per identificare l'ubicazione delle previsioni errate, né Windows avrebbe un contatore di prestazioni per questo, né ci sarebbe una parola per descriverlo, se non fosse un problema.
  • L'ignoranza sui livelli inferiori e l'architettura non rendono qualcuno che ne sia consapevole.
  • Cerca sempre di capire i limiti dell'hardware su cui stai lavorando.

Ecco un punto di riferimento per i non credenti. È meglio eseguire il processo in RealTime / High per mitigare lo scheduler che ha un effetto: https://gist.github.com/1200737


Operatore logico ( || e && ) rispetto all'operatore bitwise ( | and & ).

La differenza più cruciale tra un operatore logico e un operatore bit a bit è che un operatore logico prende due booleani e produce un booleano mentre un operatore bit a bit prende due interi e produce un intero (nota: interi indica qualsiasi tipo di dati integrale, non solo int).

Per essere pedante, un operatore bit per bit prende un modello bit (ad esempio 01101011) e fa un AND-OR bit-saggio su ciascun bit. Quindi, ad esempio se hai due interi a 8 bit:

a     = 00110010 (in decimal:    32+16+2   = 50)
b     = 01010011 (in decimal: 64+   16+2+1 = 83)
----------------
a & b = 00010010 (in decimal:       16+2   = 18)
a | b = 01110011 (in decimal: 64+32+16+2+1 = 115)

mentre un operatore logico funziona solo in bool :

a      = true
b      = false
--------------
a && b = false
a || b = true

In secondo luogo, è spesso possibile utilizzare un operatore bit a bit su bool poiché true e false equivale rispettivamente a 1 e 0 e succede che se si traduce true a 1 e false a 0, allora si esegue un'operazione bit a bit, quindi si converte non zero a vero e zero a falso; succede che il risultato sarà lo stesso se hai appena usato l'operatore logico (controlla questo per l'esercizio).

Un'altra importante distinzione è anche che un operatore logico è cortocircuitato . Quindi, in alcuni ambienti [1], vedi spesso persone che fanno qualcosa del genere:

if (person && person.punch()) {
    person.doVictoryDance()
}

che si traduce in: "se la persona esiste (cioè non è nulla), prova a picchiarlo, e se il pugno ha successo (cioè restituisce il vero), allora fai una danza della vittoria" .

Se avessi usato un operatore bit per bit, questo:

if (person & person.punch()) {
    person.doVictoryDance()
}

tradurrà in: "se la persona esiste (cioè non è nulla) e il pugno ha successo (cioè restituisce il vero), quindi esegui una danza della vittoria" .

Si noti che nell'operatore logico cortocircuitato, il codice person.punch() non può essere eseguito affatto se la person è nullo. Infatti, in questo caso particolare, il secondo codice produrrebbe un errore di riferimento null se la person è nullo, poiché prova a chiamare person.punch() indipendentemente dal fatto che la persona sia nullo o meno. Questo comportamento di non valutare l'operando corretto è chiamato cortocircuito .

[1] Alcuni programmatori si divertiranno a mettere una chiamata di funzione che ha un effetto collaterale all'interno di un'espressione if , mentre per altri è un linguaggio comune e molto utile.

Poiché un operatore bit a bit funziona su 32 bit alla volta (se si è su una macchina a 32 bit), può portare a un codice più elegante e più veloce se è necessario confrontare un numero enorme di condizioni, ad es.

int CAN_PUNCH = 1 << 0, CAN_KICK = 1 << 1, CAN_DRINK = 1 << 2, CAN_SIT = 1 << 3,
    CAN_SHOOT_GUNS = 1 << 4, CAN_TALK = 1 << 5, CAN_SHOOT_CANNONS = 1 << 6;

Person person;
person.abilities = CAN_PUNCH | CAN_KICK | CAN_DRINK | CAN_SIT | CAN_SHOOT_GUNS;

Place bar;
bar.rules = CAN_DRINK | CAN_SIT | CAN_TALK;

Place military;
military.rules = CAN_SHOOT_CANNONS | CAN_PUNCH | CAN_KICK | CAN_SHOOT_GUNS | CAN_SIT;

CurrentLocation cloc1, cloc2;
cloc1.usable_abilities = person_abilities & bar_rules;
cloc2.usable_abilities = person_abilities & military_rules;

// cloc1.usable_abilities will contain the bit pattern that matches `CAN_DRINK | CAN_SIT`
// while cloc2.usable_abilities will contain the bit pattern that matches `CAN_PUNCH | CAN_KICK | CAN_SHOOT_GUNS | CAN_SIT`

Fare lo stesso con gli operatori logici richiederebbe una quantità imbarazzante di confronti:

Person person;
person.can_punch = person.can_kick = person.can_drink = person.can_sit = person.can_shoot_guns = true;
person.can_shoot_cannons = false;

Place bar;
bar.rules.can_drink = bar.rules.can_sit = bar.rules.can_talk = true;
bar.rules.can_punch = bar.rules.can_kick = bar.rules.can_shoot_guns = bar.rules.can_shoot_cannons = false;

Place military;
military.rules.can_punch = military.rules.can_kick = military.rules.can_shoot_guns = military.rules.can_shoot_cannons = military.rules.can_sit = true;
military.rules.can_drink = military.rules.can_talk = false;

CurrentLocation cloc1;
bool cloc1.usable_abilities.can_punch         = bar.rules.can_punch         && person.can_punch,
     cloc1.usable_abilities.can_kick          = bar.rules.can_kick          && person.can_kick,
     cloc1.usable_abilities.can_drink         = bar.rules.can_drink         && person.can_drink,
     cloc1.usable_abilities.can_sit           = bar.rules.can_sit           && person.can_sit,
     cloc1.usable_abilities.can_shoot_guns    = bar.rules.can_shoot_guns    && person.can_shoot_guns,
     cloc1.usable_abilities.can_shoot_cannons = bar.rules.can_shoot_cannons && person.can_shoot_cannons
     cloc1.usable_abilities.can_talk          = bar.rules.can_talk          && person.can_talk;

bool cloc2.usable_abilities.can_punch         = military.rules.can_punch         && person.can_punch,
     cloc2.usable_abilities.can_kick          = military.rules.can_kick          && person.can_kick,
     cloc2.usable_abilities.can_drink         = military.rules.can_drink         && person.can_drink,
     cloc2.usable_abilities.can_sit           = military.rules.can_sit           && person.can_sit,
     cloc2.usable_abilities.can_shoot_guns    = military.rules.can_shoot_guns    && person.can_shoot_guns,
     cloc2.usable_abilities.can_talk          = military.rules.can_talk          && person.can_talk,
     cloc2.usable_abilities.can_shoot_cannons = military.rules.can_shoot_cannons && person.can_shoot_cannons;

Un esempio classico in cui vengono utilizzati i modelli di bit e l'operatore bit per bit è nelle autorizzazioni del file system Unix / Linux.


&& e & significano due cose molto diverse e ti danno due risposte diverse.

1 && 2 produce 1 ("vero")
1 & 2 restituisce 0 ("falso")

&& è un operatore logico - significa "vero se entrambi gli operandi sono veri"
& è un confronto bit a bit. Significa "dimmi quale dei bit è impostato in entrambi gli operandi"


Semplicemente,

if exp1 && exp2

se exp1 è flase non controllare exp2

ma

if exp1 & exp2

se exp1 è false O true check exp2

e raramente le persone usano & perché raramente vogliono controllare exp2 se exp1 è false


&& è la versione di cortocircuito di & .

Se stiamo valutando il false & true , sappiamo già dal primo argomento che il risultato sarà falso. La versione && dell'operatore restituirà un risultato non appena possibile, piuttosto che valutare l'intera espressione. C'è anche una versione simile di | operatore, || .


Il modo più veloce (e un po 'più basso) per spiegare questo a persone che non hanno bisogno di conoscere le esatte operazioni del codice quando lo fanno è

&& esegue un controllo su ciascuna di queste condizioni fino a quando non trova un falso e restituisce l'intero risultato come falso

|| sta facendo un controllo su ognuna di quelle condizioni fino a quando non trova un vero e restituisce l'intero risultato come vero.

& sta facendo un APK basato su MATHS ENTRAMBE / TUTTE le condizioni e si occupano del risultato.

| sta facendo l'APK basato su MATHS ENTRAMBI / TUTTE le condizioni e si occupano del risultato.

Non ho mai trovato un punto in cui ho avuto bisogno di usare & o | all'interno di una dichiarazione if. Lo uso principalmente per il taglio di valori esadecimali nei colori dei componenti usando lo spostamento bit per bit.

PER ESEMPIO:

r = fullvalue >> 0xFF & 0xFF;
g = fullvalue >> 0xF & 0xFF;
b = fullvalue & 0xFF;

All'interno di questa operazione "& 0xFF" impone di vedere solo il valore binario. Non ho personalmente trovato un uso per | ma comunque.





operators