ทำไมเราต้องกำหนดทั้งสอง == และ! = ใน C #


346

คอมไพเลอร์ C # ต้องการว่าเมื่อใดก็ตามที่ประเภทที่กำหนดเองจะกำหนดผู้ประกอบการ==มันจะต้องกำหนดด้วย!=(ดูที่นี่ )

ทำไม?

ฉันอยากรู้ว่าทำไมนักออกแบบจึงคิดว่าจำเป็นและเหตุใดจึงไม่สามารถเริ่มต้นคอมไพเลอร์เพื่อให้เกิดการใช้งานที่สมเหตุสมผลสำหรับผู้ประกอบการรายใดรายหนึ่ง ตัวอย่างเช่น Lua ช่วยให้คุณกำหนดตัวดำเนินการความเสมอภาคเท่านั้นและคุณจะได้รับตัวดำเนินการอื่นฟรี C # จะทำเช่นเดียวกันโดยขอให้คุณสามารถกำหนดอย่างใดอย่างหนึ่งหรือทั้งสอง == == และ! = แล้วโดยอัตโนมัติรวบรวมหายไป! = !(left == right)ผู้ประกอบการเป็น

ฉันเข้าใจว่ามีบางกรณีที่มุมแปลก ๆ ที่บางหน่วยงานอาจไม่เท่ากันหรือไม่เท่ากัน (เช่น IEEE-754 NaN) แต่สิ่งเหล่านั้นดูเหมือนเป็นข้อยกเว้นไม่ใช่กฎ ดังนั้นนี่ไม่ได้อธิบายว่าทำไมนักออกแบบคอมไพเลอร์ C # จึงได้ยกเว้นกฎ

ฉันเคยเห็นกรณีของฝีมือไม่ดีที่กำหนดตัวดำเนินการความเท่าเทียมกันจากนั้นผู้ดำเนินการความไม่เท่าเทียมกันจะเป็นสำเนาคัดลอกที่มีการเปรียบเทียบแต่ละรายการที่ย้อนกลับและทุก && เปลี่ยนเป็น | | (คุณได้รับจุด ... โดยทั่วไป! (a == b) ขยายผ่านกฎของ De Morgan) นั่นเป็นวิธีปฏิบัติที่ไม่ดีที่ผู้รวบรวมสามารถกำจัดได้โดยการออกแบบเช่นเดียวกับ Lua

หมายเหตุ: ตัวยึดเดิมสำหรับตัวดำเนินการ <> <=> = ฉันไม่สามารถจินตนาการถึงกรณีที่คุณจะต้องนิยามสิ่งเหล่านี้ด้วยวิธีที่ผิดธรรมชาติ Lua ช่วยให้คุณกำหนดได้เฉพาะ <และ <= และกำหนด> = และ> โดยธรรมชาติผ่านการปฏิเสธของ formers เหตุใด C # จึงไม่เหมือนกัน (อย่างน้อย 'โดยค่าเริ่มต้น')

แก้ไข

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

เคอร์เนลของคำถามของฉัน แต่เป็นเพราะเหตุใดจึงเป็นสิ่งจำเป็นบังคับใช้ใน C # เมื่อมักจะไม่จำเป็นต้องมีเหตุผล ?

นอกจากนี้ยังเป็นในทางตรงกันข้ามที่โดดเด่นในการออกแบบทางเลือกสำหรับ NET อินเตอร์เฟซเหมือนObject.Equals, IEquatable.Equals IEqualityComparer.Equalsที่ขาดการNotEqualsแสดงคู่ที่กรอบการพิจารณา!Equals()เป็นวัตถุที่ไม่เท่ากันและที่ว่า นอกจากนี้คลาสที่ชอบDictionaryและวิธีการเช่น.Contains()ขึ้นอยู่กับอินเทอร์เฟซดังกล่าวข้างต้นเท่านั้นและไม่ใช้ตัวดำเนินการโดยตรงแม้ว่าพวกเขาจะถูกกำหนดไว้ ในความเป็นจริงเมื่อ ReSharper สร้างความเท่าเทียมกันของสมาชิกจะกำหนดทั้งสอง==และ!=ในแง่ของEquals()และได้แล้วเฉพาะในกรณีที่เลือกใช้ในการสร้างผู้ประกอบการที่ทุกคน ตัวดำเนินการความเสมอภาคไม่จำเป็นต้องใช้โดยกรอบเพื่อทำความเข้าใจความเท่าเทียมกันของวัตถุ

โดยพื้นฐานแล้ว. NET Framework ไม่สนใจตัวดำเนินการเหล่านี้ แต่จะสนใจเพียงไม่กี่Equalsวิธี การตัดสินใจที่จะกำหนดทั้งโอเปอเรเตอร์ == และ! = จะต้องถูกกำหนดโดยผู้ใช้นั้นมีความเกี่ยวข้องกับการออกแบบภาษาอย่างแท้จริง


24
+1 สำหรับคำถามที่ยอดเยี่ยม ดูเหมือนจะไม่มีเหตุผลใด ๆ เลย ... แน่นอนว่าผู้แปลสามารถสันนิษฐานได้ว่า! = b สามารถแปลเป็น! (a == b) เว้นแต่จะมีการแจ้งเป็นอย่างอื่น ฉันอยากรู้ว่าทำไมพวกเขาถึงตัดสินใจทำเช่นนี้
Patrick87

16
นี่เป็นวิธีที่เร็วที่สุดที่ฉันเคยเห็นคำถามที่ให้คะแนนและชื่นชอบ
Christopher Currens

26
ฉันแน่ใจว่า @Eric Lippert สามารถให้คำตอบเสียงได้เช่นกัน
Yuck

17
"นักวิทยาศาสตร์ไม่ใช่คนที่ให้คำตอบที่ถูกต้องเขาเป็นคนที่ถามคำถามที่ถูกต้อง" นี่คือเหตุผลใน SE คำถามได้รับการสนับสนุน และมันใช้งานได้!
Adriano Carneiro

4
คำถามเดียวกันสำหรับ Python: stackoverflow.com/questions/4969629/…
Josh Lee

คำตอบ:


161

ฉันไม่สามารถพูดกับนักออกแบบภาษาได้ แต่จากสิ่งที่ฉันสามารถให้เหตุผลได้ดูเหมือนว่าเป็นการตัดสินใจโดยเจตนาและการออกแบบที่เหมาะสม

ดูรหัส F # พื้นฐานนี้คุณสามารถรวบรวมสิ่งนี้ลงในห้องสมุดที่ใช้งานได้ นี่คือรหัสทางกฎหมายสำหรับ F # และโอเวอร์โหลดตัวดำเนินการความเท่าเทียมกันเท่านั้นไม่ใช่ความไม่เท่าเทียมกัน:

module Module1

type Foo() =
    let mutable myInternalValue = 0
    member this.Prop
        with get () = myInternalValue
        and set (value) = myInternalValue <- value

    static member op_Equality (left : Foo, right : Foo) = left.Prop = right.Prop
    //static member op_Inequality (left : Foo, right : Foo) = left.Prop <> right.Prop

มันทำในสิ่งที่ดูเหมือน มันสร้างตัวเปรียบเทียบความเท่าเทียมกัน==เท่านั้นและตรวจสอบเพื่อดูว่าค่าภายในของชั้นเรียนเท่ากัน

ในขณะที่คุณไม่สามารถสร้างคลาสเช่นนี้ใน C # คุณสามารถใช้คลาสที่รวบรวมไว้สำหรับ. NET เห็นได้ชัดว่ามันจะใช้โอเปอเรเตอร์ที่โอเวอร์โหลดของเราเพื่อ== ใช้งานจริงรันไทม์ใช้ทำ!=อะไร

มาตรฐาน C # EMCA มีกฎทั้งหมด (ส่วนที่ 14.9) อธิบายวิธีกำหนดผู้ปฏิบัติงานที่จะใช้เมื่อประเมินความเท่าเทียมกัน หากต้องการทำให้มันง่ายเกินไปและไม่แม่นยำอย่างสมบูรณ์หากประเภทที่เปรียบเทียบนั้นเป็นประเภทเดียวกันและมีโอเปอเรเตอร์ความเท่าเทียมกันมากเกินไปก็จะใช้โอเวอร์โหลดนั้นและจะไม่ใช้ตัวดำเนินการความเสมอภาคอ้างอิงมาตรฐานที่สืบทอดจาก Object ไม่แปลกใจเลยถ้าหากมีเพียงหนึ่งในโอเปอเรเตอร์มันจะใช้โอเปอเรเตอร์การอ้างอิงความเท่าเทียมเริ่มต้นว่าวัตถุทั้งหมดมีอยู่ไม่มีการโอเวอร์โหลด 1

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

ดังนั้นทำไมคอมไพเลอร์ไม่สร้าง!=โอเปอเรเตอร์อัตโนมัติ ฉันไม่สามารถทราบได้อย่างแน่นอนเว้นแต่ว่ามีบางคนจาก Microsoft ยืนยันสิ่งนี้ แต่นี่คือสิ่งที่ฉันสามารถพิจารณาได้จากการให้เหตุผลเกี่ยวกับข้อเท็จจริง


เพื่อป้องกันพฤติกรรมที่ไม่คาดคิด

บางทีฉันต้องการเปรียบเทียบค่า==เพื่อทดสอบความเท่าเทียมกัน อย่างไรก็ตามเมื่อมาถึง!=ฉันไม่สนใจเลยถ้าค่าเท่ากันยกเว้นการอ้างอิงเท่ากันเพราะสำหรับโปรแกรมของฉันที่จะพิจารณาพวกเขาเท่ากันฉันก็แค่สนใจถ้าอ้างอิงตรงกัน ท้ายที่สุดแล้วนี่เป็นพฤติกรรมเริ่มต้นของ C # (หากผู้ประกอบการทั้งสองไม่ได้รับการโหลดมากเกินไปเช่นในกรณีของ. net บางไลบรารีที่เขียนในภาษาอื่น) หากคอมไพเลอร์เพิ่มรหัสโดยอัตโนมัติฉันไม่สามารถพึ่งพาคอมไพเลอร์กับรหัสผลลัพธ์ที่ควรสอดคล้องได้อีกต่อไป คอมไพเลอร์ไม่ควรเขียนรหัสที่ซ่อนซึ่งเปลี่ยนแปลงพฤติกรรมของคุณโดยเฉพาะเมื่อรหัสที่คุณเขียนนั้นอยู่ในมาตรฐานของทั้ง C # และ CLI

ในแง่ของมันบังคับให้คุณเกินมันแทนที่จะไปทำงานเริ่มต้นฉันเท่านั้นมั่นสามารถพูดได้ว่ามันอยู่ในมาตรฐาน (EMCA-334 17.9.2) 2 มาตรฐานไม่ได้ระบุสาเหตุ ฉันเชื่อว่านี่เป็นเพราะ C # ยืมพฤติกรรมมากจาก C ++ ดูด้านล่างสำหรับข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้


เมื่อคุณแทนที่!=และ==คุณไม่จำเป็นต้องส่งคืนบูล

นี่เป็นอีกเหตุผลที่น่าจะเป็น ใน C # ฟังก์ชันนี้:

public static int operator ==(MyClass a, MyClass b) { return 0; }

ใช้ได้เช่นเดียวกับสิ่งนี้:

public static bool operator ==(MyClass a, MyClass b) { return true; }

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


C # ยืมมามากจาก C ++ 3

เมื่อมีการแนะนำ C # มีบทความในนิตยสาร MSDN ที่เขียนขึ้นและพูดถึง C #:

นักพัฒนาหลายคนหวังว่าจะมีภาษาที่เขียนอ่านและดูแลได้ง่ายเช่น Visual Basic แต่ก็ยังให้พลังและความยืดหยุ่นของ C ++

ใช่เป้าหมายการออกแบบสำหรับ C # คือให้พลังงานเกือบเท่ากันกับ C ++ โดยเสียสละเพียงเล็กน้อยเพื่อความสะดวกเช่นความปลอดภัยแบบแข็งและการเก็บขยะ C # เป็นแบบอย่างที่ดีหลังจาก C ++

คุณอาจไม่แปลกใจที่รู้ว่าใน C ++ ตัวดำเนินการความเสมอภาคไม่จำเป็นต้องส่งคืนบูลดังแสดงในโปรแกรมตัวอย่างนี้

ตอนนี้ C ++ ไม่ต้องการให้คุณโอเวอร์โหลดโอเปอเรเตอร์เสริมโดยตรง หากคอมไพล์โค้ดของคุณในโปรแกรมตัวอย่างคุณจะเห็นมันรันโดยไม่มีข้อผิดพลาด อย่างไรก็ตามหากคุณลองเพิ่มบรรทัด:

cout << (a != b);

คุณจะได้รับ

ข้อผิดพลาดของคอมไพเลอร์ C2678 (MSVC): binary '! =': ไม่พบโอเปอเรเตอร์ที่ใช้ตัวถูกดำเนินการทางด้านซ้ายของประเภท 'ทดสอบ' (หรือไม่มีการแปลงที่ยอมรับได้) '

ดังนั้นในขณะที่ C ++ เองไม่ต้องการให้คุณใช้งานเกินพิกัดเป็นคู่มันจะไม่ยอมให้คุณใช้ตัวดำเนินการความเท่าเทียมกันที่คุณไม่ได้ใช้งานมากเกินไปในคลาสที่กำหนดเอง มันถูกต้องใน. NET เนื่องจากวัตถุทั้งหมดมีค่าเริ่มต้น C ++ ไม่ได้


1. ในฐานะที่เป็นหมายเหตุด้านมาตรฐาน C # ยังคงต้องการให้คุณโอเวอร์โหลดคู่ของโอเปอเรเตอร์หากคุณต้องการโอเวอร์โหลดทั้งคู่ นี่คือส่วนหนึ่งของมาตรฐานและไม่เพียงแค่คอมไพเลอร์ อย่างไรก็ตามกฎเดียวกันกับการพิจารณาว่าผู้ให้บริการรายใดที่จะโทรไปใช้เมื่อคุณเข้าถึงไลบรารี. net ที่เขียนในภาษาอื่นที่ไม่มีข้อกำหนดเดียวกัน

2. EMCA-334 (pdf) ( http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf )

3. และ Java แต่นั่นไม่ใช่จุดที่นี่


75
" คุณไม่ต้องกลับบูล " .. ฉันคิดว่านั่นเป็นคำตอบของเราที่นั่น; ทำได้ดี. หาก! (o1 == o2) นั้นไม่ได้กำหนดไว้อย่างดีแล้วคุณไม่สามารถคาดหวังได้ว่ามาตรฐานจะขึ้นอยู่กับมัน
Brian Gordon

10
กระแทกแดกดันในหัวข้อเกี่ยวกับตัวดำเนินการเชิงตรรกะที่คุณใช้เป็นลบสองครั้งในประโยค "C # ไม่อนุญาตให้คุณแทนที่หนึ่งใน" ซึ่งจริงๆแล้วหมายถึง "C # ช่วยให้คุณสามารถแทนที่หนึ่งในนั้น"
Dan Diplo

13
@Dan Diplo มันโอเคมันไม่ใช่ว่าฉันไม่ได้รับอนุญาตให้ทำเช่นนั้นหรือไม่
Christopher Currens

6
หากเป็นจริงฉันสงสัยว่า # 2 นั้นถูกต้อง แต่ตอนนี้กลายเป็นคำถามให้เหตุผลที่==จะกลับอะไร แต่bool? หากคุณส่งคืนintคุณจะไม่สามารถใช้มันเป็นคำสั่งแบบมีเงื่อนไขอีกต่อไป if(a == b)จะไม่รวบรวมอีกต่อไป ประเด็นคืออะไร?
BlueRaja - Danny Pflughoeft

2
คุณสามารถส่งคืนวัตถุที่มีการแปลงโดยนัยเป็นบูล
Stefan Dragnev

53

อาจเป็นเพราะถ้ามีคนต้องการใช้ตรรกะสามค่า (เช่นnull) ในกรณีเช่นนั้น - SQL มาตรฐาน ANSI เป็นต้น - ตัวดำเนินการไม่สามารถถูกละเลยได้ขึ้นอยู่กับอินพุต

คุณอาจมีกรณีที่:

var a = SomeObject();

และa == trueผลตอบแทนfalseและยังให้ผลตอบแทนa == falsefalse


1
ผมว่าในกรณีที่ว่าตั้งแต่aไม่ได้เป็นแบบบูลที่คุณควรจะเปรียบเทียบกับ enum สามรัฐไม่ได้จริงหรือtrue false
Brian Gordon

18
โดยวิธีการที่ฉันไม่สามารถเชื่อว่าไม่มีใครที่อ้างอิงFileNotFoundตลก ..
ไบรอันกอร์ดอน

28
ถ้าa == trueเป็นเช่นfalseนั้นฉันก็มักจะคาดหวังว่าa != trueจะเป็นtrueแม้ว่าจะa == falseเป็นfalse
Fede

20
@Fede กระแทกเล็บบนหัว สิ่งนี้ไม่มีอะไรเกี่ยวข้องกับคำถามคุณกำลังอธิบายว่าทำไมบางคนอาจต้องการแทนที่==ไม่ใช่ว่าทำไมพวกเขาจึงควรถูกบังคับให้แทนที่ทั้งสอง
BlueRaja - Danny Pflughoeft

6
@Yuck - ใช่มีการใช้งานเริ่มต้นที่ดี: เป็นกรณีทั่วไป แม้ในตัวอย่างของคุณการใช้งานเริ่มต้นของ!=จะถูกต้อง ในกรณีที่ผิดปกติอย่างยิ่งที่เราต้องการx == yและx != yเป็นเท็จ(ฉันไม่สามารถนึกถึงตัวอย่างเดียวที่สมเหตุสมผล)พวกเขายังสามารถแทนที่การใช้งานเริ่มต้นและให้บริการของพวกเขาเอง ทำให้กรณีทั่วไปเป็นค่าเริ่มต้นเสมอ!
BlueRaja - Danny Pflughoeft

25

นอกเหนือจาก C # ไปที่ C ++ ในหลาย ๆ ด้านแล้วคำอธิบายที่ดีที่สุดที่ฉันสามารถนึกได้ก็คือในบางกรณีคุณอาจต้องการใช้แนวทางที่แตกต่างกันเล็กน้อยในการพิสูจน์ว่า "ไม่เท่าเทียมกัน" มากกว่าการพิสูจน์ "ความเท่าเทียมกัน"

เห็นได้ชัดว่ามีการเปรียบเทียบสตริงตัวอย่างเช่นคุณสามารถทดสอบความเท่าเทียมกันและreturnออกจากลูปเมื่อคุณเห็นอักขระที่ไม่ตรงกัน อย่างไรก็ตามอาจไม่สะอาดมากนักพร้อมปัญหาที่ซับซ้อนมากขึ้น กรองบานในใจ; มันง่ายมากที่จะบอกได้อย่างรวดเร็วว่าองค์ประกอบนั้นไม่ได้อยู่ในฉากหรือไม่ แต่ยากที่จะบอกได้ว่าองค์ประกอบนั้นอยู่ในฉากหรือไม่ ในขณะที่returnเทคนิคเดียวกันสามารถนำไปใช้รหัสอาจไม่สวย


6
ตัวอย่างที่ดี; ฉันคิดว่าคุณพูดถึงเหตุผลว่าทำไม ง่าย!ล็อคคุณเข้าสู่ความหมายเดียวสำหรับความเท่าเทียมกันในกรณีที่ coder ทราบอาจจะสามารถเพิ่มประสิทธิภาพทั้งใน และ== !=
Yuck

13
ดังนั้นนี้จะอธิบายว่าทำไมพวกเขาควรจะได้รับตัวเลือกที่จะแทนที่ทั้งสองไม่ได้ว่าทำไมพวกเขาควรจะถูกบังคับให้ไป
BlueRaja - Danny Pflughoeft

1
ในหลอดเลือดดำนั้นพวกเขาไม่จำเป็นต้องปรับให้เหมาะสมทั้งคู่พวกเขาเพียงแค่ต้องให้คำจำกัดความที่ชัดเจนของทั้งคู่
Jesper

22

หากคุณดูการใช้งานเกินพิกัด == และ! = ในแหล่ง. net พวกเขามักจะไม่ใช้! = เป็น! (ซ้าย == ขวา) พวกเขาใช้มันอย่างสมบูรณ์ (เช่น ==) ด้วยตรรกะเมื่อตะกี้ ตัวอย่างเช่น DateTime ใช้ == เป็น

return d1.InternalTicks == d2.InternalTicks;

และ! = เป็น

return d1.InternalTicks != d2.InternalTicks;

หากคุณ (หรือคอมไพเลอร์ถ้ามันทำโดยปริยาย) จะต้องใช้! = เป็น

return !(d1==d2);

จากนั้นคุณกำลังตั้งสมมติฐานเกี่ยวกับการนำไปใช้ภายใน == และ! = ในสิ่งที่ชั้นเรียนของคุณอ้างถึง หลีกเลี่ยงการสันนิษฐานว่าอาจเป็นปรัชญาที่อยู่เบื้องหลังการตัดสินใจของพวกเขา


17

เพื่อตอบการแก้ไขของคุณเกี่ยวกับสาเหตุที่คุณถูกบังคับให้แทนที่ทั้งสองอย่างหากคุณลบล้างสิ่งใดสิ่งหนึ่งสิ่งนี้อยู่ในการสืบทอด

หากคุณแทนที่ == ส่วนใหญ่มีแนวโน้มที่จะจัดเรียงความหมายเชิงความหมายหรือโครงสร้างบางอย่าง (ตัวอย่างเช่น DateTimes จะเท่ากันหากคุณสมบัติ InternalTicks ของพวกเขาเท่ากันแม้ว่าพวกเขาอาจจะเป็นกรณีที่แตกต่างกัน) ดังนั้นคุณจะเปลี่ยนพฤติกรรมเริ่มต้น วัตถุซึ่งเป็นแม่ของวัตถุ. NET ทั้งหมด ตัวดำเนินการ == คือใน C # ซึ่งเป็นเมธอดซึ่งการใช้งานพื้นฐาน Object.operator (==) ทำการเปรียบเทียบการอ้างอิง Object.operator (! =) เป็นอีกวิธีหนึ่งที่แตกต่างกันซึ่งยังทำการเปรียบเทียบการอ้างอิง

ในกรณีอื่น ๆ ของวิธีการเอาชนะมันจะไม่มีเหตุผลที่จะเข้าใจว่าการเอาชนะวิธีใดวิธีหนึ่งก็จะส่งผลให้เกิดการเปลี่ยนแปลงพฤติกรรมวิธีการ antonymic หากคุณสร้างคลาสที่มี Increment () และ Decrement () วิธีและ overrode Increment () ในคลาสเด็กคุณจะคาดหวังว่า Decrement () จะถูกแทนที่ด้วยพฤติกรรมตรงข้ามของคุณหรือไม่? คอมไพเลอร์ไม่สามารถทำให้ฉลาดพอที่จะสร้างฟังก์ชันผกผันสำหรับการใช้งานตัวดำเนินการในทุกกรณีที่เป็นไปได้

อย่างไรก็ตามโอเปอเรเตอร์แม้ว่าจะใช้วิธีการในลักษณะเดียวกัน == และ! =, <และ>, และ <= และ> = มันจะไร้เหตุผลในกรณีนี้จากมุมมองของผู้บริโภคที่จะคิดว่า! = ทำงานแตกต่างจาก == ดังนั้นคอมไพเลอร์ไม่สามารถคาดเดาได้ว่า a! = b ==! (a == b) ในทุกกรณี แต่โดยทั่วไปแล้วคาดว่า == และ! = ควรทำงานในลักษณะเดียวกันดังนั้นกองกำลังคอมไพเลอร์ คุณต้องติดตั้งเป็นคู่ แต่จริงๆแล้วคุณต้องลงมือทำเช่นนั้น หากสำหรับคลาสของคุณ a! = b ==! (a == b) ให้ใช้ตัวดำเนินการ! = โดยใช้! (==) แต่ถ้ากฎนั้นไม่ได้ถือวัตถุของคุณในทุกกรณี (เช่น หากเปรียบเทียบกับค่าใดค่าหนึ่งที่เท่ากันหรือไม่เท่ากันคุณจะต้องฉลาดกว่า IDE

คำถามจริงที่ควรถามคือทำไม <และ> และ <= และ> = เป็นคู่สำหรับผู้ประกอบการเปรียบเทียบที่จะต้องดำเนินการพร้อมกันเมื่ออยู่ในเงื่อนไขตัวเลข! (a <b) == a> = b และ! (a> b) == a <= b คุณควรจะใช้ทั้งสี่หากคุณแทนที่หนึ่งและคุณควรจะต้องแทนที่ == (และ! =) เช่นกันเพราะ (a <= b) == (a == b) ถ้า a มีความหมาย เท่ากับ b


14

หากคุณโอเวอร์โหลด == สำหรับประเภทที่กำหนดเองของคุณและไม่ใช่! = มันจะถูกจัดการโดยตัวดำเนินการ! = สำหรับวัตถุ! = วัตถุเนื่องจากทุกอย่างมาจากวัตถุและสิ่งนี้จะแตกต่างจาก CustomType! = CustomType

นอกจากนี้ผู้สร้างภาษาอาจต้องการวิธีนี้เพื่อให้เกิดความยืดหยุ่นมากที่สุดสำหรับผู้เขียนโค้ดและเพื่อที่พวกเขาจะไม่ได้ตั้งสมมติฐานเกี่ยวกับสิ่งที่คุณตั้งใจจะทำ


คุณสร้างแรงบันดาลใจให้กับคำถามนี้: stackoverflow.com/questions/6919259/…
Brian Gordon

2
เหตุผลที่มีความยืดหยุ่นนอกจากนี้ยังสามารถนำไปใช้กับคุณสมบัติหลายอย่างที่ดีที่พวกเขาตัดสินใจที่จะลาออกเช่นblogs.msdn.com/b/ericlippert/archive/2011/06/23/... .Thus มันไม่ได้อธิบายทางเลือกของพวกเขาสำหรับ การดำเนินการของผู้ประกอบการความเท่าเทียมกัน
Stefan Dragnev

นั่นเป็นเรื่องที่น่าสนใจมาก ดูเหมือนว่ามันจะเป็นคุณสมบัติที่มีประโยชน์ ฉันไม่รู้ว่าทำไมพวกเขาถึงทิ้งมันไว้
Chris Mullins

11

นี่คือสิ่งที่อยู่ในใจของฉันก่อน:

  • จะเป็นอย่างไรถ้าการทดสอบความไม่เท่าเทียมนั้นเร็วกว่าการทดสอบความเท่าเทียมกันมาก?
  • เกิดอะไรขึ้นถ้าในบางกรณีคุณต้องการส่งคืนfalseทั้งสอง==และ!= (เช่นถ้าไม่สามารถเปรียบเทียบได้ด้วยเหตุผลบางอย่าง)

5
ในกรณีแรกคุณสามารถใช้การทดสอบความเท่าเทียมกันในแง่ของการทดสอบความไม่เท่าเทียมกัน
Stefan Dragnev

กรณีที่สองจะทำลายโครงสร้างเช่นDictionaryและListถ้าคุณใช้ประเภทที่กำหนดเองกับพวกเขา
Stefan Dragnev

2
@Stefan: Dictionaryใช้GetHashCodeและEqualsไม่ใช่ตัวดำเนินการ การใช้งานList Equals
Dan Abramov

6

มันอาจเป็นแค่ตัวเลือกการออกแบบ แต่อย่างที่คุณพูดx!= yไม่จำเป็นต้องเหมือนกัน!(x == y)ไม่ต้องเป็นเช่นเดียวกับการไม่เพิ่มการใช้งานเริ่มต้นทำให้คุณมั่นใจได้ว่าคุณไม่สามารถลืมนำไปใช้งานได้ และถ้ามันเป็นเรื่องไม่สำคัญอย่างที่คุณพูดคุณก็สามารถนำไปใช้กับอีกอันหนึ่งได้ ฉันไม่เห็นว่านี่เป็น 'การปฏิบัติที่ไม่ดี' อย่างไร

อาจมีความแตกต่างอื่น ๆ ระหว่าง C # และ Lua ด้วย ...


1
มันเป็นวิธีปฏิบัติที่ยอดเยี่ยมในการนำไปใช้ในแง่อื่น ๆ ฉันเพิ่งเห็นมันทำอย่างอื่น - ซึ่งเป็นข้อผิดพลาดได้ง่าย
Stefan Dragnev

3
นั่นคือปัญหาของโปรแกรมเมอร์ไม่ใช่ปัญหาของ C # โปรแกรมเมอร์ที่ไม่สามารถใช้สิทธิ์นี้จะล้มเหลวในด้านอื่น ๆ ด้วย
GolezTrol

@GolezTrol: คุณช่วยอธิบาย Lua ได้ไหม ฉันไม่คุ้นเคยกับ Lua เลย แต่การอ้างอิงของคุณดูเหมือนจะพูดอะไรบางอย่าง
zw324

@ Ziyao Wei: OP ชี้ให้เห็นว่า Lua ทำสิ่งนี้แตกต่างจาก C # (เห็นได้ชัดว่า Lua จะเพิ่มคู่หูเริ่มต้นสำหรับผู้ประกอบการที่กล่าวถึง) เพียงแค่พยายามทำให้การเปรียบเทียบเป็นเรื่องเล็กน้อย หากไม่มีความแตกต่างเลยอย่างน้อยหนึ่งคนก็ไม่มีสิทธิ์อยู่ ต้องบอกว่าฉันไม่รู้เหมือนกันลัวะ
GolezTrol

6

คำสำคัญในคำถามของคุณคือ " ทำไม " และ " ต้อง "

ผลที่ตามมา:

ตอบมันเป็นแบบนี้เพราะพวกเขาออกแบบให้เป็นจริง ... แต่ไม่ตอบ "ทำไม" เป็นส่วนหนึ่งของคำถามของคุณ

การตอบว่าบางครั้งอาจมีประโยชน์ในการแทนที่ทั้งสองอย่างนี้เป็นจริง ... แต่ไม่ใช่การตอบคำถาม "ต้อง" เป็นส่วนหนึ่งของคำถามของคุณ

ฉันคิดว่าคำตอบง่ายๆคือไม่มีเหตุผลที่น่าเชื่อถือว่าทำไม C # ต้องการให้คุณแทนที่ทั้งสอง

ภาษาที่จะช่วยให้คุณสามารถแทนที่เท่านั้น==และให้คุณเริ่มต้นใช้งานของ!=ที่เป็น!ที่ หากคุณต้องการที่จะแทนที่!=เช่นกันให้ลองทำดู

มันไม่ใช่การตัดสินใจที่ดี มนุษย์ออกแบบภาษามนุษย์ไม่สมบูรณ์แบบ C # ไม่สมบูรณ์แบบ ยักและ QED


5

เพียงเพิ่มคำตอบที่ยอดเยี่ยมที่นี่:

พิจารณาสิ่งที่จะเกิดขึ้นในดีบักเกอร์เมื่อคุณพยายามก้าวเข้าสู่!=โอเปอเรเตอร์และจบลงด้วยการ==โอเปอเรเตอร์แทน! พูดเกี่ยวกับความสับสน!

มันสมเหตุสมผลแล้วที่ CLR จะอนุญาตให้คุณมีอิสระที่จะละทิ้งตัวดำเนินการหนึ่งหรืออื่น ๆ - เนื่องจากมันต้องทำงานกับหลายภาษา แต่ก็ยังมีความอุดมสมบูรณ์ของตัวอย่างของ C # ไม่เปิดเผยคุณสมบัติ CLR ( refผลตอบแทนและชาวบ้านตัวอย่างเช่น) และความอุดมสมบูรณ์ของตัวอย่างของการใช้คุณลักษณะไม่ได้อยู่ใน CLR ตัวเอง (เช่นusing, lock, foreachฯลฯ )


3

ภาษาการเขียนโปรแกรมเป็นการปรับโครงสร้างประโยคใหม่ของคำสั่งเชิงตรรกะที่ซับซ้อนเป็นพิเศษ โดยที่ในใจคุณสามารถกำหนดกรณีของความเท่าเทียมกันโดยไม่ต้องกำหนดกรณีที่ไม่เท่าเทียมกันหรือไม่? คำตอบคือไม่ สำหรับวัตถุ a จะเท่ากับวัตถุ b ดังนั้นการผกผันของวัตถุ a ไม่เท่ากับ b จะต้องเป็นจริงเช่นกัน อีกวิธีในการแสดงสิ่งนี้คือ

if a == b then !(a != b)

สิ่งนี้ให้ความสามารถที่แน่นอนสำหรับภาษาในการกำหนดความเท่าเทียมกันของวัตถุ ตัวอย่างเช่นการเปรียบเทียบ NULL! = NULL สามารถโยนประแจลงในคำจำกัดความของระบบความเท่าเทียมที่ไม่ได้ใช้คำสั่งที่ไม่เท่าเทียมกัน

ตอนนี้เกี่ยวกับแนวคิดของ! = เพียงแค่นิยามที่สามารถเปลี่ยนได้เช่นเดียวกับใน

if !(a==b) then a!=b

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


Nullจะเป็นปัญหาเพราะnullเป็นอินพุตที่ถูกต้อง==ซึ่งไม่ควรเป็นที่แน่ชัดว่าสะดวกสบายอย่างมาก ส่วนตัวฉันคิดว่ามันช่วยให้การเขียนโปรแกรมที่คลุมเครือและคลุมเครือจำนวนมหาศาล
nicodemus13

2

ในระยะสั้นความสอดคล้องบังคับ

'==' และ '! =' เป็นสิ่งที่ตรงกันข้ามเสมอไม่ว่าคุณจะนิยามมันอย่างไรนิยามโดยวาจาว่า "เท่ากับ" และ "ไม่เท่ากับ" ด้วยการกำหนดเพียงหนึ่งในนั้นคุณเปิดตัวเองขึ้นไปถึงผู้ประกอบการที่เท่าเทียมกันซึ่งทั้ง '==' และ '! =' สามารถทั้งสองเป็นจริงหรือทั้งสองเป็นเท็จสำหรับค่าที่กำหนดสอง คุณต้องกำหนดทั้งสองอย่างตั้งแต่เมื่อคุณเลือกที่จะกำหนดอย่างใดอย่างหนึ่งคุณต้องกำหนดอีกอย่างเหมาะสมเพื่อให้ชัดเจนว่านิยามของ "ความเท่าเทียมกัน" ของคุณนั้นชัดเจนเพียงใด โซลูชันอื่นสำหรับคอมไพเลอร์คืออนุญาตให้คุณแทนที่ '==' OR '! =' เท่านั้นและปล่อยให้โซลูชันอื่น ๆ เห็นได้ชัดว่านั่นไม่ใช่กรณีของคอมไพเลอร์ C # และฉันแน่ใจว่ามี '

คำถามที่คุณควรถามคือ "ทำไมฉันต้องแทนที่โอเปอเรเตอร์?" นั่นคือการตัดสินใจที่แข็งแกร่งที่จะต้องใช้เหตุผลที่แข็งแกร่ง สำหรับวัตถุ '==' และ '! =' เปรียบเทียบโดยการอ้างอิง หากคุณต้องการแทนที่พวกเขาเพื่อไม่เปรียบเทียบโดยการอ้างอิงคุณกำลังสร้างตัวดำเนินการทั่วไปที่ไม่สอดคล้องกับผู้พัฒนารายอื่นซึ่งจะอ่านโค้ดนั้นไม่ได้ หากคุณพยายามถามคำถาม "เป็นสถานะของอินสแตนซ์ทั้งสองนี้ที่เทียบเท่ากันหรือไม่" คุณควรใช้ IEquatible ให้กำหนด Equals () และใช้การเรียกใช้เมธอดนั้น

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

แก้ไข: นี่เป็นเพียงเหตุผลของฉัน


นี่คือไร้เหตุผล หากเหตุผลที่มีความสอดคล้องกันแล้วตรงข้ามจะใช้: คุณเท่านั้นที่จะสามารถที่จะดำเนินการและคอมไพเลอร์จะอนุมานoperator == ท้ายที่สุดoperator !=นี่คือสิ่งที่มันทำเพื่อoperator +และ operator +=
Konrad Rudolph

1
foo + = bar; เป็นการกำหนดแบบผสมชวเลขสำหรับ foo = foo + bar ;. มันไม่ได้เป็นผู้ประกอบการ
blenderfreaky

หากพวกเขาเป็น 'ตรงกันข้ามจริงเสมอ' นั่นก็เป็นเหตุผลที่จะนิยามโดยอัตโนมัติa != bว่า!(a == b)... (ซึ่งไม่ใช่สิ่งที่ C # ทำ)
andrewf

-3

อาจเป็นแค่บางสิ่งที่พวกเขาไม่คิดว่าจะไม่มีเวลาทำ

ฉันมักจะใช้วิธีการของคุณเมื่อฉันเกิน == จากนั้นฉันก็ใช้มันในอีกอันหนึ่ง

คุณพูดถูกด้วยการทำงานเพียงเล็กน้อยผู้แปลสามารถมอบสิ่งนี้ให้เราฟรี

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