Чому в C # існує як коротке замикання, так і нестандартне зміна цього оператора?


9

Періодично мені цікаво про це:

ІЛИ коротке замикання завжди повертало б те саме значення, що і оператор АБО з нестандартним замиканням?

Я очікую, що АБО завжди буде оцінюватися швидше. Отож, чи був включений оператор безконтактних схем АБО на мову C # для узгодженості?

Що я пропустив?


9
Те саме значення повернення - так. Ті ж побічні ефекти - ні.
робота

2
Припустимо, f()викликає виняток, врахуйте true || f()і true | f(). Ви бачите різницю? Перший вираз оцінює true, а оцінка останнього призводить до викиду винятку.
шарфридж

Відповіді:


25

Обидва операнди були призначені для різних речей, що надходили від C, який не мав булевого типу. Версія короткого замикання || працює лише з булевими, в той час як версія для короткого замикання | працює з інтегральними типами, виконуючи побітові або Це просто сталося як логічна операція без короткого замикання для булей, які представлені одним бітом, який дорівнює 0 або 1.

http://en.wikibooks.org/wiki/C_Sharp_Programming/Operators#Logical


10
+1 для посилання. Коли я читав це питання, мені здавалося, що "що?" оскільки afaik офіційна назва їх є логічними та бітовими або, а не короткими та нестандартними ланцюгами, які, як виявляються, є побічними ефектами того, як вони працюють.
stijn

1
+1 Я бачу, що "працює лише на булевих операндах" - я, можливо, не помітив різниці, оскільки я в будь-якому разі використовую лише вирази, які оцінюють булеві
CarneyCode

2
Мені довелося перевірити, але насправді це правда, що |оператор при застосуванні до двох булевих значень компілюється в один і той же orоператор в CIL, як і при застосуванні до двох цілих значень - на відміну від того, ||який компілюється в CIL з використанням brtrueдля умовного стрибати.
квентин-зірин

13

|(розрядне або) повинно бути не коротким замиканням для таких типів int. Це тому, що майже у всіх випадках вам потрібно обчислити обидві сторони |виразу, щоб обчислити правильний результат. Наприклад, що є результатом 7 | f(x)?

Якщо f(x)це не оцінено, ви не можете сказати.

Крім того, було б непослідовно робити цей оператор коротким замиканням bool, коли воно не є коротким int. До речі, я думаю, що я ніколи |навмисно не використовував логічні порівняння, вважаючи, що говорити про |логічного оператора дуже незручно .

|| однак це стосується логічних порівнянь, де оцінка короткого замикання працює добре.

Це ж справедливо навіть для C і C ++, де походження цих операторів.


5

Це правильно, оператор АБО короткого замикання (||) завжди буде повертати те саме значення, що й оператор АБО, що не має короткого замикання (|) (*)

Однак якщо перший операнд істинний, оператор короткого замикання не спричинить оцінку другого операнда, тоді як оператор, що не має короткого замикання, завжди спричинятиме оцінку обох операндів. Це може мати вплив на продуктивність, а іноді і на побічні ефекти.

Отже, є користь для обох: якщо ви дбаєте про продуктивність, а оцінка другого операнда не спричиняє жодних побічних ефектів, (або якщо ви не переймаєтесь ними), то будь-якими способами використовуйте оператор короткого замикання. . Але якщо з якихось причин вам потрібні побічні ефекти другого операнда, то вам слід скористатися оператором без короткого замикання.

Приклад, коли слід використовувати оператор без короткого замикання:

if( write_customer_to_database() != SUCCESS |
    write_supplier_to_database() != SUCCESS |
    write_order_to_database() != SUCCESS )
{
    transaction_rollback();
}

(*) За винятком деяких дійсно перекручених сценаріїв, коли оцінка першого операнда на хибні причини викликає побічний ефект, другий операнд оцінює як істинний, а не хибний.


2
+1 Але я хотів би додати, що крім використання функцій для вказівки на успіх чи невдачу (як у вашому прикладі): функцій із побічними ефектами, як правило, слід уникати ...
Marjan Venema

1
Так, це, мабуть, тому я ніколи до сьогодні не використовував жодного з операторів, що не мають короткого замикання, і шанси невеликі, що я коли-небудь буду.
Майк Накіс

Цей приклад залежить від операндів, що оцінюються в суворому порядку зліва направо. C # гарантує це, але багато подібних мов (включаючи C і C ++) не відповідають цим.
Кіт Томпсон

Чи не має значення для короткого замикання сенсу для цього конкретного прикладу, оскільки всі транзакції будуть відмовлені, якщо будь-яка з них не вдасться? (Я роблю деякі припущення щодо семантики дзвінків.)
Кіт Томпсон,

@KeithThompson ви маєте рацію, оцінка короткого замикання призведе до непотрібної обробки, і в цьому сенсі приклад трохи кульгавий, але це не так кульгаво, щоб вимагати зміни, оскільки правда залишається такою, якщо оцінка короткого замикання, якщо write_customer_to_database () досягає успіху, тоді решта функцій запису _... ніколи не буде викликана, і в разі успіху краще працювати правильно, ніж виконувати деякі непотрібні операції у разі відмови.
Майк Накіс

4

Існує 2 причини, щоб використовувати не короткий ланцюг варіант:

  1. збереження побічних ефектів (але це зрозуміліше кодувати тимчасовою змінною)

  2. запобігають тимчасовим атакам , уникаючи вилки в короткому замиканні (це може навіть підвищити ефективність виконання, якщо другий операнд є змінною, але це мікрооптимізація, яку компілятор може зробити в будь-якому випадку)


Це єдина
вірна

2

якщо в правому пункті оператора є побічний ефект, і програміст задумав, що він хоче, щоб обидва побічні ефекти відбулися, перш ніж перевірити їх повернене значення.


5
Еек. Будь ласка, не кодуйте так… якщо ви покладаєтесь на побічні ефекти, маєте два різних вирази, призначте результати та протестуйте їх після цього .
Конрад Рудольф
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.