ทำไม "&&" ไม่ใช่ "&"


135

เหตุใดจึงเป็นที่&&นิยม&และ||ดีกว่า|?

ฉันถามคนที่เขียนโปรแกรมมาหลายปีแล้วคำอธิบายของเขาคือ:

ยกตัวอย่างเช่นในif (bool1 && bool2 && bool3) { /*DoSomething*/ }, bool1มีที่จะเป็นจริงสำหรับการไปทดสอบbool2ที่มีจะเป็นจริงก่อนจะย้ายไปbool3ฯลฯ หากผมใช้เพียงครั้งเดียว&แทนที่จะมีคำสั่งในการทดสอบแม้ว่าทั้งหมดของพวกเขาจะต้องมีความจริงที่จะไม่มี ก้าวไปสู่บรรทัดถัดไปแล้วทำไมมันถึงสำคัญล่ะ?

หมายเหตุ: ฉันต้องการชี้ให้เห็นว่าฉันเทียบเท่ากับการเขียนโปรแกรมของเด็กวัยหัดเดินและนี่ไม่ใช่คำถามที่จริงจังหรือเร่งด่วน เป็นเรื่องของการทำความเข้าใจมากกว่าว่าเหตุใดจึงควรทำสิ่งต่าง ๆ ในทางตรงกันข้าม


55
& และ && | และ || เป็นตัวดำเนินการที่แตกต่างกันอย่างสิ้นเชิง
Felice Pollano

3
Retagged เนื่องจากไม่ได้ใช้กับ C # เท่านั้น
Jonathan Dickinson

12
เปลี่ยนกลับเนื่องจากคำตอบมีความเฉพาะเจาะจงสำหรับ C # อยู่แล้วและการทำงานภายในอาจแตกต่างกันเล็กน้อยในภาษาอื่นที่โดยทั่วไปมีแนวคิดเดียวกัน
Daniel Hilgarth

8
@Felice: แตกต่างกัน แต่แทบจะไม่แตกต่างกันเลย ในความเป็นจริงมันคล้ายกันมากx & yและx && yจะประเมินผลลัพธ์เดียวกันเสมอถ้า x และ y เป็นนิพจน์ประเภทบูลีน ในความเป็นจริงความแตกต่างเพียงอย่างเดียวในกรณีนั้นดูเหมือนว่าในx & yy จะถูกประเมินเสมอ
Joren

1
@slawekin: ฉันขอแนะนำให้อ่านคำตอบ บางคนเขียนถึงความแตกต่างของประสิทธิภาพอย่างกว้างขวาง คำตอบอาจทำให้คุณประหลาดใจ
Abel

คำตอบ:


183

ในกรณีส่วนใหญ่&&และ||เป็นที่ต้องการมากกว่า&และ|เนื่องจากอดีตเป็นกระแสไฟฟ้าลัดวงจรหมายความว่าการประเมินผลจะถูกยกเลิกทันทีที่ผลลัพธ์ชัดเจน

ตัวอย่าง:

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, ... ) เพราะพวกเขาเป็นผู้ประกอบการระดับบิต &เป็นตัวดำเนินการบิตและบิตและ|เป็นตัว ดำเนินการ หรือบิต

เพื่อให้ถูกต้องมากใน C # ตัวดำเนินการเหล่านั้น ( &, |[และ^]) เรียกว่า "ตัวดำเนินการเชิงตรรกะ" (ดูข้อกำหนดC #บทที่ 7.11) มีการใช้งานหลายอย่างของตัวดำเนินการเหล่านี้:

  1. สำหรับจำนวนเต็ม ( int, uint, longและulongบท 7.11.1):
    พวกเขาจะดำเนินการในการคำนวณผลค่าที่เหมาะสมของตัวถูกดำเนินการและผู้ประกอบการคือ&คือใช้ในการคำนวณค่าที่เหมาะสมตรรกะANDฯลฯ
  2. สำหรับการแจงนับ (บทที่ 7.11.2):
    ถูกนำไปใช้เพื่อดำเนินการทางตรรกะของประเภทการแจงนับ
  3. สำหรับบูลและบูลที่เป็นโมฆะ (บทที่ 7.11.3 และ 7.11.4):
    ผลลัพธ์ไม่ได้ถูกคำนวณโดยใช้การคำนวณแบบบิต ผลลัพธ์จะถูกค้นหาโดยพื้นฐานตามค่าของตัวถูกดำเนินการทั้งสองเนื่องจากจำนวนความเป็นไปได้นั้นน้อยมาก
    เนื่องจากใช้ทั้งสองค่าสำหรับการค้นหาการใช้งานนี้จึงไม่ลัดวงจร

31
นอกจากนี้ยังมีประโยชน์ในการตรวจสอบว่ามีบางสิ่งเป็นโมฆะหรือไม่ ตัวอย่างเช่น: if(op != null && op.CanExecute()). เนื่องจากสาเหตุที่สองไม่ได้รับการประเมินเมื่อสาเหตุแรกไม่เป็นจริงจึงถูกต้อง
TJHeuvel

2
@TJHeuvel: นี่เป็นการใช้งานแบบเดียวกับที่ฉันอธิบายไว้กับTryGetValueตัวอย่างของฉัน แต่ใช่มันเป็นอีกหนึ่งตัวอย่างที่ดีในเรื่องนี้
Daniel Hilgarth

4
คำตอบที่ดี. บางทีคุณควรเพิ่มตัวอย่างวิธี&หรือ|ใช้กับอาร์กิวเมนต์ที่ไม่ใช่บูล (เช่นสิ่งที่ตัวดำเนินการทำ) เพื่อประโยชน์ของทุกคนใหม่
Zabba

81

เพื่ออธิบายให้ชัดเจนว่านี่หมายถึงอะไร (แม้ว่าคำตอบอื่น ๆ จะบอกใบ้ - แต่อาจใช้คำศัพท์ที่คุณไม่เข้าใจ)

รหัสต่อไปนี้:

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

รวบรวมไว้เพื่อสิ่งนี้จริงๆ:

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

โดยที่รหัสต่อไปนี้รวบรวมตรงตามที่แสดง:

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

สิ่งนี้เรียกว่าการลัดวงจร โดยทั่วไปคุณควรใช้&&และ||อยู่ในเงื่อนไขของคุณเสมอ

เครื่องหมายโบนัส:มีสถานการณ์หนึ่งที่คุณไม่ควรทำ หากคุณอยู่ในสถานการณ์ที่ประสิทธิภาพเป็นสิ่งสำคัญ (และนี่เป็นสิ่งสำคัญในระดับนาโนวินาที ) ให้ใช้การลัดวงจรเมื่อคุณต้อง (เช่นการnullตรวจสอบ) เนื่องจากการลัดวงจรเป็นกิ่งไม้ / กระโดด ซึ่งอาจส่งผลให้การคาดเดาสาขาบน CPU ของคุณผิดพลาด ถูกกว่ากว่า& &&นอกจากนี้ยังมีสถานการณ์ที่การลัดวงจรสามารถทำลายตรรกะได้ - ลองดูคำตอบนี้ของฉัน

Diatribe / Monologue : เกี่ยวกับการทำนายผิดสาขาที่ส่วนใหญ่ละเลยอย่างมีความสุข อ้างถึงAndy Firth (ซึ่งทำงานเกี่ยวกับเกมมา 13 ปีแล้ว): "นี่อาจเป็นระดับที่ต่ำกว่าที่ผู้คนคิดว่าพวกเขาต้องไป ... แต่พวกเขาคิดผิดการทำความเข้าใจว่าฮาร์ดแวร์ที่คุณกำลังเขียนโปรแกรมสำหรับการปฏิบัติต่อสาขาสามารถทำได้อย่างไร ส่งผลกระทบต่อประสิทธิภาพการทำงานในระดับมาก ... มากกว่าที่โปรแกรมเมอร์ส่วนใหญ่อาจชื่นชม re: death โดยการตัดเป็นพันครั้ง "

  • นักพัฒนาเกม (และคนอื่น ๆ ที่ทำงานในสภาพเรียลไทม์ที่รุนแรง) ไปไกลถึงการปรับโครงสร้างตรรกะของพวกเขาเพื่อให้เหมาะกับตัวทำนาย นอกจากนี้ยังมีหลักฐานในรหัส mscorlib ที่ถอดรหัสแล้ว
  • เพียงเพราะ. NET ปกป้องคุณจากสิ่งประเภทนี้ไม่ได้หมายความว่ามันไม่สำคัญ การทำนายผิดสาขามีราคาแพงมากที่ 60 Hz หรือ 10,000 คำขอ / วินาที
  • Intel จะไม่มีเครื่องมือในการระบุตำแหน่งของการคาดการณ์ที่ผิดพลาดและ Windows จะไม่มีตัวนับประสิทธิภาพสำหรับสิ่งนี้และไม่มีคำอธิบายมันก็ไม่ใช่ปัญหา
  • ความไม่รู้เกี่ยวกับระดับล่างและสถาปัตยกรรมไม่ได้ทำให้คนที่ตระหนักถึงพวกเขาผิดพลาด
  • พยายามทำความเข้าใจข้อ จำกัด ของฮาร์ดแวร์ที่คุณใช้งานอยู่เสมอ

นี่คือเกณฑ์มาตรฐานสำหรับผู้ไม่เชื่อ วิธีที่ดีที่สุดคือเรียกใช้กระบวนการในแบบเรียลไทม์ / สูงเพื่อลดการจัดตารางเวลาที่มีผลกระทบ: https://gist.github.com/1200737


7
เกี่ยวกับ "คะแนนโบนัส": เราทุกคนรู้ดีว่าเกิดจากการเพิ่มประสิทธิภาพก่อนกำหนด :)
ผู้ใช้

6
@ ไมเคิล - นั่นคือเหตุผลที่ 'นาโนวินาทีสำคัญ' เป็นตัวหนา :) นักพัฒนาเกม AAA มักจะกังวลกับเรื่องแบบนี้และคุณไม่มีทางรู้ว่าใครจะอ่านคำตอบ ดังนั้นจึงเป็นการดีที่สุดเสมอที่จะบันทึกแม้กระทั่งกรณีที่มีเส้นเขตแดน / กรณีรุนแรง
Jonathan Dickinson

1
เครื่องหมายโบนัสนั้นใช้ได้กับ C # หรือไม่? ฉันไม่ได้คิดว่าเนื่องจาก MSIL ถูกตีความเว้นแต่ว่านิพจน์จะถูกรวบรวมลงในรหัสเครื่อง
Jeremy McGee

7
@Jeremy MSIL ไม่ได้ตีความ
Jonathan Dickinson

2
@TheD ตรวจสอบคำตอบอีกครั้ง - ฉันได้เพิ่มคำพูดคนเดียวเกี่ยวกับสาเหตุที่คุณควรกังวลเกี่ยวกับเรื่องนี้ และ FYI (x && y)แปลLOAD x; BRANCH_FALSE; LOAD y; BRANCH_FALSE;ว่า(x & y)แปลตรงLOAD x; LOAD y; AND; BRANCH_FALSE;ไหน สาขาเดียวกับสอง
Jonathan Dickinson

68

ตัวดำเนินการทางตรรกะ ( ||และ&&) เทียบกับตัวดำเนินการแบบบิต ( |และ&)

ความแตกต่างที่สำคัญที่สุดระหว่างตัวดำเนินการเชิงตรรกะและตัวดำเนินการแบบบิตคือตัวดำเนินการเชิงตรรกะรับสองบูลีนและสร้างบูลีนในขณะที่ตัวดำเนินการแบบบิตรับจำนวนเต็มสองจำนวนและสร้างจำนวนเต็ม (หมายเหตุ: จำนวนเต็มหมายถึงชนิดข้อมูลที่เป็นหนึ่งใด ๆ ไม่ใช่เฉพาะ 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

ประการที่สองมักเป็นไปได้ที่จะใช้ตัวดำเนินการแบบบิตบนบูลเนื่องจากจริงและเท็จเทียบเท่ากับ 1 และ 0 ตามลำดับและเกิดขึ้นว่าหากคุณแปลค่าจริงเป็น 1 และเท็จเป็น 0 ให้ทำการดำเนินการแบบบิตจากนั้นแปลงค่าที่ไม่ใช่ศูนย์ เป็นจริงและศูนย์เป็นเท็จ มันเกิดขึ้นที่ผลลัพธ์จะเหมือนกันหากคุณเพิ่งใช้ตัวดำเนินการเชิงตรรกะ (ตรวจสอบสิ่งนี้เพื่อการออกกำลังกาย)

อีกความแตกต่างที่สำคัญยังเป็นที่ผู้ประกอบการตรรกะลัดวงจร ดังนั้นในบางแวดวง [1] คุณมักจะเห็นคนทำสิ่งนี้:

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

ซึ่งแปล: "ถ้าคนที่มีอยู่ (เช่นไม่ null) พยายามที่จะชกเขา / เธอและถ้าหมัดประสบความสำเร็จ (เช่นผลตอบแทนจริง) แล้วทำเต้นรำชัยชนะ"

หากคุณใช้ตัวดำเนินการ bitwise แทนสิ่งนี้:

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

จะแปล: "ถ้าคนที่มีอยู่ (เช่นไม่ null) และหมัดประสบความสำเร็จ (เช่นผลตอบแทนจริง) แล้วทำเต้นรำชัยชนะ"

โปรดสังเกตว่าในตัวดำเนินการลอจิคัลลัดวงจรperson.punch()รหัสอาจไม่ถูกรันเลยถ้าpersonเป็นโมฆะ ในความเป็นจริงในกรณีนี้รหัสที่สองจะสร้างข้อผิดพลาดในการอ้างอิงที่personเป็นโมฆะหากเป็นค่าว่างเนื่องจากพยายามเรียก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


3
+1 สำหรับด้านข้างมีผลต่อปัญหา แปลกใจที่ไม่ได้กล่าวถึงก่อนหน้านี้
Conrad Frix

3
ตัวอย่างดูรุนแรงเล็กน้อย แต่ดูเหมือนว่าคำตอบอื่น ๆ จะเน้นไปที่การลัดวงจรมากเกินไปและไม่เพียงพอกับความแตกต่างระหว่างการดำเนินการกับจำนวนเต็มและบูลีน
R0MANARMY

ต้องเข้าใจฟังก์ชั่นก่อนที่จะมีการใช้งานรายละเอียด (short-circtuit / ผลข้างเคียง) ดีใจที่คุณเคลียร์ความแตกต่างหลักที่เป็นตรรกะบูลีนเทียบกับจำนวนเต็มไม่ใช่การลัดวงจร
Abel

@ROMANARMY - รุนแรงฉันรักการประชดที่มอบให้คุณ ทำได้ดี
brumScouse

8

ในกรณีของ:

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

จะทำงานตามที่คาดไว้

แต่:

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

อาจทำให้เกิดข้อยกเว้นการอ้างอิงที่เป็นโมฆะ


2

สั้นและง่าย:

1 && 2= true
เพราะ
1 = true (ไม่ใช่ศูนย์) ใน C
2 = true (ไม่ใช่ศูนย์) ใน C

trueands เหตุผลด้วยที่จะให้truetrue

แต่

1 & 2= 0 = false
เพราะ
1 = 0001 ในไบนารี
2 = 0010 ในไบนารี

0001 ANDs bitwise ด้วย 0010 เพื่อให้ 0000 = 0 เป็นทศนิยม

เช่นเดียวกันสำหรับ || และ | โอเปอเรเตอร์ด้วย ... !


2
-1: เรากำลังพูดถึง C # ที่นี่ ... 1 && 2ผิดกฎหมายใน C #
Daniel Hilgarth

แต่นี่เป็นตัวอย่างที่สำคัญอย่างยิ่งที่อธิบายว่าทำไมคุณไม่สามารถแลกเปลี่ยน & และ && (ซึ่งหลายคนดูเหมือนจะคิด)
bobobobo

1

&&เป็นเวอร์ชันลัดวงจรของ&.

หากเรากำลังประเมินfalse & trueเรารู้แล้วจากการดูอาร์กิวเมนต์แรกว่าผลลัพธ์จะเป็นเท็จ &&รุ่นของผู้ประกอบการจะกลับมาส่งผลให้เร็วที่สุดเท่าที่สามารถจะทำได้มากกว่าการประเมินการแสดงออกทั้ง นอกจากนี้ยังมี verion ของที่คล้ายกันผู้ประกอบการ|||


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

มีความปลอดภัย

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

จะผิดพลาดหากรายการไม่มีขนาดที่เหมาะสม


ฉันนึกไม่ถึงว่าจะมีใครเขียน "if (list.Count ()> 14 & list [14] ==" foo ")" แทน "if (list.Count ()> 14 && list [14] ==" ฟู ")". & เพียงแค่และเป็นธรรมชาติไม่สามารถใช้สำหรับ && ได้ในกรณีนี้แม้ว่า & & # 39; s จะปลอดภัยอย่างแน่นอน (เช่นรายการ [1])
Tien Do

1

ตัวดำเนินการ C #ควรอธิบายว่าเหตุใด:

โดยพื้นฐานแล้วการมีสอง&'หรือ|' หมายความว่ามันเป็นเงื่อนไขมากกว่าตรรกะดังนั้นคุณสามารถบอกความแตกต่างระหว่างสองสิ่งนี้ได้

และผู้ประกอบการ&มีตัวอย่างของการใช้หนึ่ง


ลิงก์ทั้งสอง (อย่างมีประสิทธิภาพ) ใช้งานไม่ได้ (เปลี่ยนเส้นทางไปยัง"Visual Studio 2005 Retired documentation" )
Peter Mortensen

1

ตกลงตามมูลค่าที่ตราไว้

    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อย่ากังวลกับการตรวจสอบคีย์ซึ่งอาจใช้เวลานานกว่านี้ ..


1

เมื่อใช้ในนิพจน์เชิงตรรกะเช่นคำสั่ง if &&ควรเป็นเพราะจะหยุดการประเมินนิพจน์ทันทีที่พบผลลัพธ์ที่เป็นเท็จครั้งแรก เป็นไปได้เนื่องจากค่าเท็จจะทำให้นิพจน์ทั้งหมดเป็นเท็จ ในทำนองเดียวกัน (และอีกครั้งในนิพจน์เชิงตรรกะ) ||เป็นที่ต้องการเนื่องจากจะหยุดการประเมินนิพจน์ทันทีที่พบนิพจน์จริงเนื่องจากค่าจริงใด ๆ จะทำให้นิพจน์ทั้งหมดเป็นจริง

อย่างไรก็ตามหากนิพจน์ที่เป็นหรือแก้ไขหรือและแก้ไขร่วมกันมีผลข้างเคียงและคุณต้องการให้สิ่งเหล่านี้เกิดขึ้นจากการแสดงออกของคุณ (โดยไม่คำนึงถึงผลลัพธ์ของนิพจน์เชิงตรรกะ) จากนั้น&และ|สามารถใช้ได้ ในทางกลับกันตัวดำเนินการ&&และ||สามารถมีประโยชน์ในการป้องกันผลข้างเคียงที่ไม่ต้องการ (เช่นตัวชี้ว่างที่ทำให้เกิดข้อยกเว้น)

ตัวดำเนินการ&และ|ยังสามารถใช้กับจำนวนเต็มและในกรณีนี้จะสร้างผลลัพธ์จำนวนเต็มซึ่งเป็นตัวถูกดำเนินการสองตัวและ -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

ในภาษาอื่นที่ไม่ใช่ภาษา C # ต้องใช้ความระมัดระวังกับโหมดตรรกะและบิตของ & และ | ในโค้ดด้านบนifนิพจน์เงื่อนไขของคำสั่ง(a & 4) != 0เป็นวิธีที่ปลอดภัยในการแสดงเงื่อนไขนี้ แต่ในภาษา C เช่นเดียวกับหลายภาษาคำสั่งเงื่อนไขสามารถถือว่าค่าจำนวนเต็มศูนย์เป็นค่าจำนวนเต็มเท็จและไม่ใช่ค่าศูนย์เป็นจริงได้ (เหตุผลนี้เกี่ยวข้องกับหน่วยประมวลผลสาขาเงื่อนไขคำแนะนำที่มีอยู่และความสัมพันธ์ของพวกเขาเพื่อธงเป็นศูนย์ที่มีการปรับปรุงหลังจากการดำเนินการทุกจำนวนเต็ม.) ดังนั้น การทดสอบคำสั่งของศูนย์สามารถถอดออกและเงื่อนไขที่อาจจะลงไปìf(a & 4)

สิ่งนี้อาจทำให้เกิดความสับสนและอาจเกิดปัญหาได้เมื่อนิพจน์รวมกันโดยใช้ค่าบิตและตัวดำเนินการส่งคืนค่าที่ไม่มีบิตที่เรียงกัน พิจารณาตัวอย่างต่อไปนี้ที่ต้องการผลข้างเคียงของสองฟังก์ชันก่อนที่จะตรวจสอบว่าทั้งสองประสบความสำเร็จ (ตามที่กำหนดโดยส่งคืนค่าที่ไม่ใช่ศูนย์):

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

ใน C ถ้าfoo()ส่งกลับ 1 และbar()ส่งกลับ 2 "บางสิ่ง" จะไม่ทำเพราะ1 & 2เป็นศูนย์

C # ต้องการคำสั่งแบบifมีเงื่อนไขเช่นมี oeprand บูลีนและภาษาไม่อนุญาตให้แคสต์ค่าจำนวนเต็มเป็นค่าบูลีน ดังนั้นโค้ดด้านบนจะสร้างข้อผิดพลาดของคอมไพเลอร์ มันจะแสดงได้ถูกต้องมากขึ้นดังนี้:

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

1

หากคุณเป็นโปรแกรมเมอร์จับเวลาเก่า C, ต้องระวัง C # ทำให้ฉันประหลาดใจจริงๆ

MSDNกล่าวสำหรับตัว|ดำเนินการ:

ไบนารี | ตัวดำเนินการถูกกำหนดไว้ล่วงหน้าสำหรับประเภทอินทิกรัลและบูล สำหรับประเภทอินทิกรัล | คำนวณบิตหรือของตัวถูกดำเนินการ สำหรับตัวถูกดำเนินการบูล | คำนวณตรรกะ OR ของตัวถูกดำเนินการ นั่นคือผลลัพธ์จะเป็นเท็จก็ต่อเมื่อตัวถูกดำเนินการทั้งสองเป็นเท็จ

(การเน้นย้ำเป็นของฉัน) ประเภทบูลีนได้รับการจัดการเป็นพิเศษและในบริบทนี้คำถามก็เริ่มสมเหตุสมผลเท่านั้นและความแตกต่างก็คือเช่นเดียวกับอื่น ๆ ที่มีอยู่แล้วในคำตอบของพวกเขา:

&&และ||กำลังลัดวงจร &และ|ประเมินตัวถูกดำเนินการทั้งสอง

และสิ่งที่ดีกว่านั้นขึ้นอยู่กับหลาย ๆ อย่างเช่นผลข้างเคียงประสิทธิภาพและความสามารถในการอ่านโค้ด แต่โดยทั่วไปแล้วตัวดำเนินการลัดวงจรจะดีกว่าเพราะคนที่มีภูมิหลังคล้ายกันเช่นฉันเข้าใจได้ดีกว่า

เหตุผลคือ: ฉันจะโต้แย้งเช่นนี้: เนื่องจากไม่มีประเภทบูลีนที่แท้จริงใน C คุณสามารถใช้ตัวดำเนินการแบบบิต|และประเมินผลลัพธ์ว่าเป็นจริงหรือเท็จในเงื่อนไข if แต่นี่เป็นทัศนคติที่ไม่ถูกต้องสำหรับ C # เนื่องจากมีกรณีพิเศษสำหรับประเภทบูลีนอยู่แล้ว


0

เป็นสิ่งสำคัญเพราะหากค่าใช้จ่ายในการประเมิน bool2 (เช่น) สูง แต่ bool1 เป็นเท็จคุณก็ช่วยตัวเองในการคำนวณได้พอสมควรโดยใช้ && over &


0

เพราะ&&และ||จะใช้สำหรับการควบคุมการไหลเช่นเดียวกับif/elseที่มี ไม่ได้เกี่ยวกับเงื่อนไขเสมอไป มีความสมเหตุสมผลอย่างยิ่งที่จะเขียนเป็นคำสั่งไม่ใช่ifหรือเป็นwhileเงื่อนไขดังต่อไปนี้:

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

หรือแม้กระทั่ง

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

ไม่ใช่แค่ว่าจะพิมพ์ได้ง่ายกว่าif/elseเวอร์ชันที่เท่ากัน นอกจากนี้ยังอ่านและเข้าใจได้ง่ายกว่ามาก


0

&& และ & หมายถึงสองสิ่งที่แตกต่างกันมากและให้คำตอบที่แตกต่างกันสองข้อ

1 && 2ให้ 1 ("true")
1 & 2ให้ผลเป็น 0 ("false")

&&เป็นตัวดำเนินการตรรกะ - หมายความว่า "จริงถ้าตัวถูกดำเนินการทั้งสองเป็นจริง"
&เป็นการเปรียบเทียบแบบบิต หมายความว่า "บอกฉันว่าบิตใดถูกตั้งค่าในตัวถูกดำเนินการทั้งสอง"


2
คำถามเกี่ยวกับ C # ใน C # ไม่มีทางที่จะส่งตัวเลขไปยังบูลดังนั้น 0 จึงไม่ใช่ 'เท็จ' และไม่ใช่ศูนย์ไม่ใช่ 'จริง' ไม่มีความเท่าเทียมกัน
Nate CK

ในการแปลงตัวเลขเป็นบูลในลักษณะที่ 1 หมายถึงจริงและ 0 หมายถึงเท็จให้พูดว่า "n! = 0" (ฉันถือว่า ... ฉันไม่ค่อยคุ้นเคยกับ C #) อันที่จริงฉันต้องการถอนความคิดเห็นนี้ออกเนื่องจากยังไม่ได้รับการค้นคว้าอย่างดีและฉันไม่คิดว่ามันจะเป็นประโยชน์หรือเกี่ยวข้องกับความคิดเห็นก่อนหน้านี้จริงๆตอนนี้ฉันคิดเกี่ยวกับเรื่องนี้มากขึ้น แต่ฉันเผลอกด Enter และตอนนี้ฉันไม่คิดว่าจะทำได้ ยกเลิกไปเลยไม่ว่ามันจะคุ้มแค่ไหน :-)
ดอนฟัก

1 && 2ให้ข้อผิดพลาดของคอมไพเลอร์: "Error 4 Operator '&&' ไม่สามารถใช้กับตัวถูกดำเนินการประเภท 'int' และ 'int'"
Peter Mortensen

0

วิธีที่เร็วที่สุด (และมึนงงเล็กน้อย) ในการอธิบายสิ่งนี้ให้กับผู้ที่ไม่จำเป็นต้องรู้การทำงานที่แน่นอนของโค้ดเมื่อทำสิ่งนี้คือ

&&กำลังตรวจสอบเงื่อนไขเหล่านั้นจนกว่าจะพบว่าเป็นเท็จและส่งคืนผลลัพธ์ทั้งหมดเป็นเท็จ

|| กำลังตรวจสอบเงื่อนไขเหล่านั้นจนกว่าจะพบว่าเป็นจริงและส่งคืนผลลัพธ์ทั้งหมดเป็นจริง

&กำลังทำตาม MATHS โดยใช้ทั้งสองเงื่อนไข / เงื่อนไขทั้งหมดและจัดการกับผลลัพธ์

| กำลังทำตามเงื่อนไขทั้งสองแบบ MATHS / เงื่อนไขทั้งหมดและจัดการกับผลลัพธ์

ฉันไม่เคยเจอจุดที่ต้องใช้&หรือ| ภายในคำสั่ง if ส่วนใหญ่ฉันใช้เพื่อตัดค่าเลขฐานสิบหกเป็นสีส่วนประกอบโดยใช้การเลื่อนระดับบิต

เช่น:

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

ภายในการดำเนินการ "& 0xFF" นี้บังคับให้ดูเฉพาะค่าไบนารีเท่านั้น ฉันไม่พบการใช้งานสำหรับ| แม้ว่า


0

เพียงแค่

if exp1 && exp2

ถ้า exp1 flase ไม่ต้องตรวจสอบ exp2

แต่

if exp1 & exp2

ถ้า exp1 เป็นfalseหรือtrue ตรวจสอบ exp2

และไม่ค่อยมีคนใช้&เพราะไม่ค่อยต้องการตรวจสอบ exp2 ว่า exp1 เป็นอย่างไรfalse

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.