Почему && и не &

Почему && предпочтительнее & и || предпочтительнее |?

Я спросил кого-то, кто программировал в течение многих лет, и его объяснение было:

Например, в if (bool1 && bool2 && bool3) { /*DoSomething*/ } значение bool1 должно быть истинным для проверки bool2, которое должно быть истинным, прежде чем перейти к bool3 и т.д. Если бы я использовал один & вместо этого нет порядка для теста, даже если все они должны быть правдивыми, чтобы перейти к следующей строке, так почему это имеет значение в любом случае?

Примечание. Я хотел бы указать, что я являюсь программным эквивалентом малыша, и это не является серьезным или срочным вопросом, это скорее вопрос понимания того, почему все должно быть сделано определенным образом, а не другим.

Ответ 1

В большинстве случаев && и || предпочтительнее, чем & и |, потому что первые закорочены, что означает, что оценка отменяется, как только результат становится ясным.

Пример:

if(CanExecute() && CanSave())
{
}

Если CanExecute возвращает false, полное выражение будет false, независимо от возвращаемого значения CanSave. Из-за этого CanSave не выполняется.

Это очень удобно при следующих обстоятельствах:

string value;
if(dict.TryGetValue(key, out value) && value.Contains("test"))
{
    // Do Something
}

TryGetValue возвращает false, если указанный ключ не найден в словаре. Из-за короткого замыкания &&, value.Contains("test") выполняется только тогда, когда TryGetValue возвращает true и, следовательно, value не null. Если вместо этого вы будете использовать побитовый оператор И &, вы получите NullReferenceException, если ключ не найден в словаре, потому что вторая часть выражения выполняется в любом случае.

Аналогичным, но более простым примером этого является следующий код (как упоминалось в TJHeuvel):

if(op != null && op.CanExecute())
{
    // Do Something
}

CanExecute выполняется только в том случае, если op не null. Если op null, первая часть выражения (op != null) оценивается как false, а оценка остального (op.CanExecute()) пропускается.

Кроме того, технически они тоже разные:
&& и || могут использоваться только на bool, тогда как & и | могут использоваться для любого интегрального типа (bool, int, long, sbyte,...), поскольку они являются побитовыми операторами. & - побитовый оператор И, а | - побитовый оператор OR.

Чтобы быть точным, в С# эти операторы (&, |^]) называются "логическими операторами" (см. С# spec, глава 7.11). Существует несколько реализаций этих операторов:

  • Для целых чисел (int, uint, long и ulong, глава 7.11.1):
    Они реализованы для вычисления побитового результата операндов и оператора, т.е. & реализует для вычисления побитового логического AND и т.д.
  • Для перечислений (глава 7.11.2):
    Они реализованы для выполнения логической операции базового типа перечисления.
  • Для bools и nullable bools (главы 7.11.3 и 7.11.4):
    Результат не вычисляется с использованием побитовых вычислений. Результат в основном проверяется на основе значений двух операндов, потому что количество возможностей настолько невелико.
    Поскольку оба значения используются для поиска, эта реализация не является короткозамкнутой.

Ответ 2

Чтобы четко объяснить, что это значит (хотя другие ответы намекают на это, но, вероятно, используйте терминологию, которую вы не понимаете).

Следующий код:

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

Действительно скомпилирован:

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

Если следующий код скомпилирован точно так, как он представлен:

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

Это называется короткозамкнутым. В общем случае вы всегда должны использовать && и || в своих условиях.

Бонусные знаки: Существует один сценарий, когда вы не должны. Если вы находитесь в ситуации, когда производительность имеет решающее значение (и это важный момент в наносекундах), используйте только короткое замыкание, когда вы должны (например, null проверка) - поскольку короткое замыкание является ветвью/Прыгать; что может привести к неверному прогнозу вашего процессора; a & намного дешевле, чем &&. Существует также сценарий, когда короткое замыкание может фактически нарушить логику - посмотрите на этот ответ моего.

Редактировать/Диатриба/Монолог. Что касается ошибочного предсказания ветки, которое блаженно игнорирует. Цитата Энди Ферт (который работает над играми в течение 13 лет): "Это может быть более низкий уровень, который люди думают, что им нужно идти.. но они были бы неправильными. Понимание того, как аппаратное обеспечение, которое вы программируете для веток, может повлиять на производительность в ОГРОМНОЙ степени... гораздо больше, чем большинство программистов могут оценить re: смерть на тысячу разрезов".

  • Разработчики игр (и другие, работающие в экстремальных условиях в режиме реального времени) доходят до реструктуризации своей логики, чтобы лучше соответствовать прогнозу. Это также свидетельствует об этом в декомпилированном коде mscorlib.
  • Просто потому, что .Net защищает вас от этого типа вещей, это не значит, что это не важно. Неправильное предсказание ветки ужасно дорого при 60 Гц; или 10000 запросов в секунду.
  • У Intel не было бы инструментов для определения местоположения ошибочных предсказаний, и у Windows не было бы супер-счетчика для этого, и не было бы слова, чтобы описать его, если бы это не проблема.
  • Незнание о более низких уровнях и архитектуре не делает того, кто знает о них неправильно.
  • Всегда старайтесь понять ограничения оборудования, над которым вы работаете.

Изменить: Вот эталон для неверующих. Лучше всего запустить процесс в RealTime/High, чтобы смягчить планировщик, имеющий эффект: https://gist.github.com/1200737

Ответ 3

логический оператор (||, & &) против побитового оператора (|, &)

Самое важное различие между логическим оператором и побитовым оператором состоит в том, что логический оператор принимает два логических значения и создает логическое, тогда как побитовый оператор принимает два целых числа и производит целое число (примечание: integers означает любой целостный тип данных, а не только int).

Чтобы быть педантичным, побитовый оператор принимает бит-шаблон (например, 01101011) и выполняет бит-бит AND/OR для каждого бита. Так, например, если у вас есть два 8-битных целых числа:

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)

в то время как логический оператор работает только в bool:

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

Во-вторых, часто можно использовать побитовый оператор на bool, так как true и false эквивалентны 1 и 0 соответственно, и бывает, что если вы переводите true в 1 и false в 0, тогда выполняете побитовое управление, -zero до true и от нуля до false; бывает так, что результат будет таким же, если бы вы просто использовали логический оператор (проверьте это для упражнений).

Еще одно важное отличие состоит в том, что логический оператор закорочен. Таким образом, в некоторых кругах [1] вы часто видите, что люди делают что-то вроде этого:

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

что соответствует: if person exists (i.e. is not null), try to punch him, and if the punch succeeds (i.e. returns true), then do a victory dance

если бы вы использовали побитовый оператор, это:

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

переведет на: if person exists (i.e. is not null) and the punch succeeds (i.e. returns true), then do a victory dance.

Обратите внимание, что в короткозамкнутом логическом операторе код person.punch() может вообще не запускаться, если человек имеет значение null. Фактически, в этом конкретном случае второй код создавал бы нулевую ошибку ссылки, если человек имеет значение null, поскольку он пытается вызвать person.punch() независимо от того, является ли человек нулевым или нет. Такое поведение при отсутствии правильного операнда называется короткое замыкание.

[1] некоторые программисты будут использовать для вызова функции, которые имеют побочный эффект внутри выражения if, тогда как для других это общая и очень полезная идиома.

Так как побитовый оператор работает по 32 бита за раз (если вы на 32-битной машине), это может привести к более элегантному и более быстрому коду, если вам нужно сравнить огромное количество условий, например

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`

делать то же самое с логическими операторами потребовало бы неудобного количества сравнений:

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;

Классический пример, в котором используются битовые шаблоны и побитовый оператор, находится в разрешении файловой системы Unix/Linux.

Ответ 4

В случае:

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

будет работать как ожидалось.

Но:

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

может потенциально выбросить исключение с нулевой ссылкой.

Ответ 5

Короткий и простой

1 && 2= true
потому что
1 = true (отличное от нуля) в c
2 = true (отличное от нуля) в c

true ANDS логически с true, чтобы дать true

но

1 & 2= 0 = false
потому что
1 = 0001 в двоичном формате 2 = 0010 в двоичном формате

0001 ANDS поразеть с 0010, чтобы дать 0000 = 0 в десятичной форме

Аналогично для || и | операторы тоже..!!

Ответ 6

Операторы С# должны объяснить, почему:

По существу, наличие двух & или | означает, что это скорее условное, чем логическое, поэтому вы можете определить разницу между ними.

& Оператор имеет пример использования одного &.

Ответ 7

&& - это версия короткого замыкания &.

Если мы оцениваем false & true, мы уже знаем, глядя на первый аргумент, что результат будет ложным. Оператор && оператора вернет результат, как только он сможет, а не оценит все выражение. Существует также аналогичный вариант оператора |, ||.

Ответ 8

if (list.Count() > 14 && list[14] == "foo")

безопасно

if (list.Count() > 14 & list[14] == "foo")

произойдет сбой, если список не имеет нужного размера.

Ответ 9

ОК, по номиналу

    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);

выдаст тот же ответ. Однако, как вы показали, если у вас есть более сложный вопрос, так:

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

Если a неверно и, возможно, b - это функция, в которой он должен уйти, подключиться к чему-то, получить это, сделать это, принять решение.. зачем беспокоиться? Отходы времени, вы знаете, это уже не удалось. Зачем делать машину и делать лишнюю бессмысленную работу?

Я всегда использовал &&, потому что я поставил, скорее всего, сбой первым, ergo, меньше вычислений, прежде чем двигаться дальше, когда нет смысла. Если нет возможности прогнозировать менее вероятные варианты, например, у вас есть логическое значение для ограничения вывода данных, например:

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

Если это не limit, не пытайтесь проверить ключ, который может занять больше времени.

Ответ 10

При использовании в логическом выражении, таком как оператор if &&, предпочтительнее, потому что он перестает оценивать выражения сразу после первого ложного результата. Это возможно, потому что ложное значение приведет к тому, что все выражение будет ложным. Точно так же (и снова в логических выражениях) || является предпочтительным, поскольку он перестает оценивать выражения, как только он встречает истинное выражение, потому что любое истинное значение приведет к тому, что все выражение будет истинным.

Если, однако, выражения, являющиеся or-ed или or-ed вместе, имеют побочные эффекты, и вы хотите, чтобы все это произошло в результате вашего выражения (независимо от результата логического выражения), затем & и |. И наоборот, операторы && и || могут быть полезны в качестве защиты от нежелательных побочных эффектов (таких как нулевой указатель, вызывающий исключение).

Операторы & и | также могут использоваться с целыми числами, и в этом случае они производят целочисленный результат, который является двумя операндами и-ed или or-ed вместе на уровне бит. Это может быть полезно, когда двоичные биты целочисленного значения используются как массив значений true и false. Чтобы проверить, включен или выключен определенный бит, битовая маска побитовая и-ed со значением. Чтобы включить бит, одна и та же маска может быть побитовой или -ed со значением. Наконец, чтобы повернуть бит, побитовое дополнение (используя ~) маски побитовое и-ed со значением.

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

В других языках, кроме С#, необходимо соблюдать логические и побитовые режимы и и |. В приведенном выше коде условное выражение if (a & 4) != 0 является безопасным способом выражения этого условия, но на многих языках типа C условные операторы могут просто рассматривать нулевые целые значения как значения false и non-zero integer как истинные. (Причина этого связана с доступными инструкциями процессора условных ветвей и их отношением к флагом нуля, который обновляется после каждой операции с целыми числами.) Таким образом, тест оператора ìf для нуля можно удалить, а условие можно сократить до (a & 4).

Это может вызвать путаницу и, возможно, даже проблемы, когда выражения объединяются с использованием побитовых и операторных значений, которые не имеют битов, которые выстраиваются в линию. Рассмотрим следующий пример, когда побочные эффекты двух функций желательны, прежде чем проверять, что они были успешными (как определено им, возвращая ненулевое значение):

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

В C, если foo() возвращает 1 и bar() возвращает 2, "что-то" не будет выполнено, потому что 1 & 2 равен нулю.

С# требует, чтобы условные операторы, такие как if, имели булевский символ, а язык не позволял отличать целочисленное значение к логическому значению. Таким образом, код выше генерирует ошибки компилятора. Правильнее было бы это выразить следующим образом:

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

Ответ 11

Это важно, потому что если стоимость оценки bool2 (например) высока, но bool1 является ложной, то вы сохранили себе справедливый бит вычисления, используя && & over &

Ответ 12

Поскольку && и || используются для управления потоками, как и if/else. Это не всегда об условностях. Вполне логично писать как инструкцию, а не как условие if или while, следующее:

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

или даже

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

Не только то, что они легче набирать, чем эквивалентные версии if/else; они также намного легче читать и понимать.

Ответ 13

& & и означают две разные вещи и дают вам два разных ответа.

1 && 2 дает 1 ( "true" )
1 & 2 дает 0 ( "false" )

&& - логический оператор - это означает "true, если оба операнда верны"
& - побитовое сравнение. Это означает "скажите, какой из битов установлен в обоих операндах"

Ответ 14

Самый быстрый (и слегка подавленный) способ объяснить это людям, которым НЕ НУЖНО знать точные операции с кодом при этом,

& выполняет проверку каждого из этих условий До находит ложное значение и возвращает весь результат как false

|| выполняет проверку каждого из этих условий Пока не найдет истину и вернет весь результат как истинный.

& делает MATHS на основе apon BOTH/ALL условий и имеет дело с результатом.

| делает MATHS на основе apon BOTH/ALL условий и имеет дело с результатом.

Я никогда не сталкивался с тем, что мне нужно было использовать и или | в операторе if. В основном я использую его для вырезания шестнадцатеричных значений в цвета его компонентов с помощью побитового сдвига.

EG:

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

В рамках этой операции "& 0xFF" вынуждает рассматривать только двоичное значение. Я лично не нашел использование для |, хотя.

Ответ 15

Просто

if exp1 && exp2

если exp1 flase не проверяйте exp2

но

if exp1 & exp2

если exp1 - false Или true проверить exp2

и редко люди используют &, потому что они редко хотят проверить exp2, если exp1 is false

Ответ 16

Если вы программист С-старожила, будьте осторожны. С# действительно удивил меня.

MSDN говорит для оператора |:

Двоичный | операторы предопределены для интегральных типов и bool. Для интегральных типов | вычисляет побитовое ИЛИ его операндов. Для операндов bool | вычисляет логический ИЛИ своих операндов; то есть результат является ложным тогда и только тогда, когда оба его операнда ложны.

(Акцент - мой.) Булевы типы обрабатываются специально, и в этом контексте вопрос только начинает иметь смысл, и разница в том, что другие уже были изложены в их ответах:

&& и || являются короткозамкнутыми. & и | оцените оба операнда.

и то, что предпочтительнее, зависит от многих факторов, таких как побочные эффекты, производительность и читаемость кода, но обычно операторы короткого замыкания предпочтительнее и потому, что они лучше понятны людям со сходным фоном, подобным мне.

Причина такова: я бы аргументировал следующим образом: поскольку в C нет реального булева типа, вы можете использовать побитовый оператор | и оценивать его результат как правдивый или фальшивый в условии if. Но это неправильное отношение для С#, потому что для булевых типов уже есть специальный случай.