Порядок логики C# и поведение компилятора

В C# (и не стесняйтесь отвечать на другие языки) в каком порядке среда выполнения оценивает логический оператор?

Пример:

DataTable myDt = new DataTable();
if (myDt != null && myDt.Rows.Count > 0)
{
    //do some stuff with myDt
}

Какой оператор рантайм оценивает первым -

myDt != null

or:

myDt.Rows.Count > 0

?

Есть ли время, когда компилятор когда-либо оценивал оператор в обратном порядке? Возможно, когда задействован оператор «ИЛИ»?


& известен как логический побитовый оператор и всегда будет оценивать все подвыражения

Каков хороший пример использования побитового оператора вместо «короткого замыкания»?


person Seibar    schedule 07.08.2008    source источник


Ответы (18)


C#: слева направо, и обработка останавливается, если обнаруживается несоответствие (оценивается как false).

person ZombieSheep    schedule 07.08.2008
comment
Как заявляли другие, короткое замыкание оператора && происходит при false. - person Broam; 13.04.2010

«C#: слева направо, и обработка останавливается, если найдено совпадение (оценивается как истинное)».

Овца-зомби ошибается, недостаточно представителей, чтобы проголосовать за нее.

Вопрос касается оператора &&, а не || оператор.

В случае && оценка остановится, если будет найдено ЛОЖЬ.

В случае || оценка останавливается, если найдено TRUE.

person Brian Leahy    schedule 08.08.2008
comment
Ага. Вы совершенно правы. Я надеюсь, что мой первоначальный ответ был правильно истолкован. У меня, конечно, не было цели ввести кого-то в заблуждение. - person ZombieSheep; 20.08.2008

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

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

  1. Это нарушает механизм ленивых вычислений, потому что перегрузка — это функция, которую нужно вызывать, и, следовательно, перед вызовом функции оцениваются оба параметра.
  2. Порядок оценки указанных параметров не гарантируется и может зависеть от компилятора. Следовательно, объекты не будут вести себя так же, как в примерах, перечисленных в вопросе/предыдущих ответах.

Для получения дополнительной информации прочитайте книгу Скотта Мейерса More Effective С++. Ваше здоровье!

person OJ.    schedule 07.08.2008

vb.net

if( x isNot Nothing AndAlso x.go()) then
  1. Оценка производится слева направо
  2. Оператор AndAlso гарантирует, что правая сторона будет оцениваться только в том случае, если левая сторона была TRUE (ИСТИНА) (очень важно, так как если x ничего не значит, x.go рухнет)

Вы можете использовать And вместо AndAlso в vb. в этом случае левая сторона также оценивается первой, но правая сторона будет оцениваться независимо от результата.

Рекомендация: всегда используйте AndAlso, если только у вас нет веской причины не делать этого.


В последующем был задан вопрос, почему и когда кто-либо может использовать And вместо AndAlso (или & вместо &&): Вот пример:

if ( x.init() And y.init()) then
   x.process(y)
end 
y.doDance()

В этом случае я хочу инициализировать и X, и Y. Y должен быть инициализирован, чтобы y.DoDance мог выполняться. Однако в функции init() я также делаю некоторые дополнительные вещи, такие как проверка открытия сокета, и только если это работает нормально, для оба, я должен продолжить и выполнить x.process (у).

Опять же, это, вероятно, не нужно и не элегантно в 99% случаев, поэтому я сказал, что по умолчанию следует использовать AndAlso.

person csmba    schedule 07.08.2008

@shsteimer

Концепция скромности относится к перегрузке оператора. в утверждении: ... A оценивается первым, если оно оценивается как false, B никогда не оценивается. То же самое относится к

Это не перегрузка оператора. Перегрузка оператора — это термин, который позволяет вам определять пользовательское поведение для операторов, таких как *, +, = и т. д.

Это позволит вам написать свой собственный класс «Журнал», а затем сделать

a = new Log(); // Log class overloads the + operator
a + "some string"; // Call the overloaded method - otherwise this wouldn't work because you can't normally add strings to objects.

Делая это

a() || b() // be never runs if a is true

на самом деле называется Оценка короткого замыкания

person Orion Edwards    schedule 07.08.2008

ZombieSheep мертв. Единственная «загвоздка», которая может ждать, заключается в том, что это верно только в том случае, если вы используете оператор &&. При использовании оператора & оба выражения будут оцениваться каждый раз, независимо от того, одно из них или оба оцениваются как ложные.

if (amHungry & whiteCastleIsNearby)
{
   // The code will check if White Castle is nearby
   // even when I am not hungry
}

if (amHungry && whiteCastleIsNearby)
{
   // The code will only check if White Castle is nearby
   // when I am hungry
}
person Brad Tutterow    schedule 07.08.2008

Обратите внимание, что существует разница между && и & относительно того, какая часть вашего выражения оценивается.

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

& известен как логический побитовый оператор и всегда будет оценивать все подвыражения.

Как таковой:

if (a() && b())

Будет вызывать b только в том случае, если a возвращает true.

однако это:

if (a() & b())

Всегда будет вызывать как a, так и b, даже если результат вызова a ложный и, следовательно, известен как false независимо от результата вызова b.

Такая же разница существует для || и | операторы.

person Lasse V. Karlsen    schedule 07.08.2008

В некоторых языках есть интересные ситуации, когда выражения выполняются в другом порядке. Я имею в виду именно Ruby, но я уверен, что они позаимствовали его откуда-то еще (вероятно, из Perl).

Выражения в логике останутся слева направо, но, например:

puts message unless message.nil?

Вышеприведенное оценит "message.nil?" во-первых, затем, если он оценивается как ложный (за исключением случаев, когда он выполняется, когда условие является ложным, а не истинным), будет выполнено «помещает сообщение», которое выводит содержимое переменной сообщения на экран.

Иногда это довольно интересный способ структурировать ваш код... Мне лично нравится использовать его для очень коротких 1-строчных лайнеров, подобных приведенным выше.

Редактировать:

Чтобы было немного понятнее, приведенное выше то же самое, что и:

unless message.nil?
  puts message
end
person Mike Stone    schedule 07.08.2008

Левый, затем останавливается, если он равен нулю.

Изменить: в vb.net он будет оценивать оба и, возможно, выдавать ошибку, если вы не используете AndAlso

person Shawn    schedule 07.08.2008

Концепция скромности относится к перегрузке оператора. в заявлении:

if( A && B){
    // do something
}

A оценивается первым, если он оценивается как false, B никогда не оценивается. То же самое относится к

if(A || B){
    //do something
}

A оценивается первым, если оно оценивается как true, B никогда не оценивается.

Эта концепция перегрузки применима (я думаю) ко всем языкам стиля C, а также ко многим другим.

person shsteimer    schedule 07.08.2008

Нет, по крайней мере, компилятор С# не работает в обратном направлении (ни в &&, ни в ||). Это слева направо.

person Vaibhav    schedule 07.08.2008

Когда все в порядке, они выполняются слева направо.

Когда вещи вложены, они выполняются изнутри наружу. Это может показаться запутанным, поскольку обычно то, что «внутреннее», находится с правой стороны строки, поэтому кажется, что оно идет в обратном направлении...

Например

a = Foo( 5, GetSummary( "Orion", GetAddress("Orion") ) );

Происходят такие вещи:

  • Вызовите GetAddress с буквальным "Orion"
  • Вызов GetSummary с литералом "Orion" и результатом GetAddress
  • Вызов Foo с литералом 5 и результатом GetSummary
  • Присвойте это значение a
person Orion Edwards    schedule 07.08.2008

Мне нравятся ответы Ориона. Добавлю две вещи:

  1. Слева направо по-прежнему применяется в первую очередь
  2. Внутренний к внешнему, чтобы гарантировать, что все аргументы разрешены перед вызовом функции

Скажем, у нас есть следующий пример:

a = Foo(5, GetSummary("Orion", GetAddress("Orion")),
           GetSummary("Chris", GetAddress("Chris")));

Вот порядок выполнения:

  1. GetAddress("Orion")
  2. GetSummary("Orion", ...)
  3. GetAddress("Chris")
  4. GetSummary("Chris", ...)
  5. Foo(...)
  6. Назначает a

Я не могу говорить о юридических требованиях C# (хотя перед написанием этого поста я протестировал аналогичный пример с Mono), но в Java этот порядок гарантирован.

И просто для полноты картины (поскольку это тоже независимый от языка поток), есть такие языки, как C и C++, где порядок не гарантируется, если нет точки последовательности. Ссылки: 1, 2. Однако при ответе на вопрос потока && и || являются точками последовательности в C++ (если они не перегружены; см. также отличный ответ OJ). Итак, несколько примеров:

  • foo() && bar()
  • foo() & bar()

В случае && foo() гарантированно запустится раньше bar() (если последний запустится вообще), поскольку && является точкой последовательности. В случае & такой гарантии нет (в C и C++), и действительно bar() может выполняться до foo() или наоборот.

person Chris Jester-Young    schedule 07.08.2008

Каков хороший пример использования побитового оператора вместо «короткого замыкания»?

Предположим, у вас есть флаги, скажем, для атрибутов файла. Предположим, вы определили READ как 4, WRITE как 2 и EXEC как 1. В двоичном виде это:

READ  0100  
WRITE 0010  
EXEC  0001

Каждый флаг имеет один набор битов, и каждый из них уникален. Побитовые операторы позволяют комбинировать эти флаги:

flags = READ & EXEC; // value of flags is 0101
person Bruce Alderman    schedule 07.08.2008

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

person GateKiller    schedule 07.08.2008

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

Обратите внимание, что & и | работает как для побитовых масок, так и для логических значений, а не только для побитовых операций. Они называются побитовыми, но в C# они определены как для целых, так и для логических типов данных.

person Lasse V. Karlsen    schedule 07.08.2008

@csmba:

В последующем был задан вопрос, почему или когда кто-либо может использовать And вместо AndAlso (или & вместо &&): Вот пример:

if ( x.init() And y.init()) then
   x.process(y)
end 
y.doDance()

В этом случае я хочу инициализировать и X, и Y. Y должен быть инициализирован, чтобы y.DoDance мог выполняться. Однако в функции init() я также делаю некоторые дополнительные вещи, такие как проверка открытия сокета, и только если это работает нормально для обоих, я должен продолжить и выполнить x.process(y).

Я считаю, что это довольно запутанно. Хотя ваш пример работает, это не типичный случай использования And (и я, вероятно, напишу это по-другому, чтобы было понятнее). And (& в большинстве других языков) на самом деле является побитовой операцией "и". Вы могли бы использовать его для вычисления битовых операций, например, удаления бита флага или маскирования и тестирования флагов:

Dim x As Formatting = Formatting.Bold Or Formatting.Italic
If (x And Formatting.Italic) = Formatting.Italic Then
    MsgBox("The text will be set in italic.")
End If
person Konrad Rudolph    schedule 20.08.2008

Язык программирования D выполняет оценку слева направо с коротким замыканием и не допускает перегрузку && и операторы '||'.

person BCS    schedule 14.04.2010