เหตุใดจึงมักเห็น“ null! = variable” แทน“ variable! = null” ใน C #?


103

ใน c # มีความแตกต่างในความเร็วในการกำจัดสำหรับลำดับที่คุณระบุเงื่อนไขหรือไม่?

if (null != variable) ...
if (variable != null) ...

ตั้งแต่ไม่นานมานี้ฉันเห็นคนแรกค่อนข้างบ่อยและมันดึงดูดความสนใจของฉันตั้งแต่ฉันคุ้นเคยกับอันที่สอง

ถ้าไม่มีข้อแตกต่างข้อแรกมีข้อดีอย่างไร?



ภาษาโปรแกรมบางภาษาสนับสนุนการเริ่มต้นค่าในเงื่อนไข if ในภาษาดังกล่าวหากเราต้องการเปรียบเทียบ LValue กับ RValue ควรใช้ตัวอักษรทางด้านซ้ายเช่น if (1 == i) โดยบังเอิญถ้าเราใส่ = ค่อนข้าง == แสดงว่าคอมไพเลอร์ผิด
Jugal Panchal

คำตอบ:


161

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

// Probably wrong
if (x = 5)

เมื่อคุณอาจจะหมายถึงจริงๆ

if (x == 5)

คุณสามารถแก้ไขสิ่งนี้ใน C โดยทำ:

if (5 == x)

การพิมพ์ผิดที่นี่จะส่งผลให้รหัสไม่ถูกต้อง

ตอนนี้ใน C # นี่คือ piffle ทั้งหมด เว้นแต่คุณจะเปรียบเทียบค่าบูลีนสองค่า (ซึ่งหายาก IME) คุณสามารถเขียนโค้ดที่อ่านได้มากขึ้นเนื่องจากคำสั่ง "if" ต้องใช้นิพจน์บูลีนในการเริ่มต้นและประเภทของ " x=5" คือInt32ไม่ใช่Booleanไม่ได้

ฉันขอแนะนำว่าหากคุณเห็นสิ่งนี้ในรหัสของเพื่อนร่วมงานคุณควรให้ความรู้แก่พวกเขาในรูปแบบของภาษาสมัยใหม่และขอแนะนำให้พวกเขาเขียนรูปแบบที่เป็นธรรมชาติมากขึ้นในอนาคต


6
เพื่อนร่วมงานของฉันทุกคนทำให้ฉันรู้สึกแย่กับสิ่งนี้และต้องการวงเล็บปีกกาแม้กระทั่งสำหรับคำสั่งเดียวหลังจาก if ... (ในกรณีที่คุณเพิ่มบางอย่างในภายหลัง 'โดยไม่รู้ตัว') หมุน F # และ Boo (และ YAML) หากภาษาของคุณไม่สามารถอ่านได้โดยไม่มีการเยื้องให้เป็นส่วนหนึ่งของภาษาฉันพูด
Benjol

48
การเพิ่มเครื่องหมายวงเล็บมีความสมเหตุสมผล IMO - C # ไม่ได้พยายามใด ๆ ที่จะหยุดไม่ให้เป็นปัญหา ซึ่งแตกต่างอย่างมากกับปัญหา "ค่าคงที่ == ตัวแปร"
Jon Skeet

6
a if (นี่! = นั่น) {performAsSuch (); } ค่อนข้างดีต่อสุขภาพจริงๆ ข้อโต้แย้งของตะเข็บ "การเพิ่มในภายหลัง" สำหรับฉันเกี่ยวกับความเปราะบางพอ ๆ กับการไม่หลีกเลี่ยง / ระบุว่า (a = 5) พิมพ์ผิด
jpinto3912

3
@jpinto: ฉันไม่ได้จริงๆว่าสิ่งที่คุณกำลังพยายามที่จะบอกว่าที่นี่ - ที่คุณทำเช่นการจัดฟันรอบงบเดียวหรือว่าคุณทำไม่ได้? ความแตกต่างระหว่างสิ่งนี้กับธุรกิจตัวแปรในคำถามคือใน C # รหัสที่พิมพ์ผิดเป็นรหัสที่ไม่ถูกต้องดังนั้นคอมไพเลอร์จะหยุดการทำงาน เช่นเดียวกับที่ไม่ได้เป็นความจริงที่ไม่ได้ใส่วงเล็บใน.
จอนสกีต

3
@Benjol อย่าลืมใส่else { }ประโยคว่างไว้เสมอเผื่อว่ามีคนต้องการเพิ่มบางอย่างในภายหลังและลืมเขียนelseคีย์เวิร์ดด้วยตัวเอง (โจ๊ก)
Jeppe Stig Nielsen

12

มีเหตุผลที่ดีที่จะใช้ null ก่อน: if(null == myDuck)

หากคุณclass Duckแทนที่==โอเปอเรเตอร์ก็if(myDuck == null)สามารถเข้าสู่ลูปที่ไม่มีที่สิ้นสุดได้

การใช้nullครั้งแรกใช้ตัวเปรียบเทียบความเท่าเทียมเริ่มต้นและทำในสิ่งที่คุณตั้งใจ

(ฉันได้ยินว่าคุณคุ้นเคยกับการอ่านโค้ดที่เขียนแบบนั้นในที่สุด - ฉันยังไม่เคยสัมผัสกับการเปลี่ยนแปลงนั้น)

นี่คือตัวอย่าง:

public class myDuck
{
    public int quacks;
    static override bool operator ==(myDuck a, myDuck b)
    {
        // these will overflow the stack - because the a==null reenters this function from the top again
        if (a == null && b == null)
            return true;
        if (a == null || b == null)
            return false;

        // these wont loop
        if (null == a && null == b)
            return true;
        if (null == a || null == b)
            return false;
        return a.quacks == b.quacks; // this goes to the integer comparison
    }
}

11
นี่เป็นสิ่งที่ไม่ถูกต้อง โหวตลดลง เพียงชี้เมาส์ไปที่==ตัวดำเนินการและวางไว้ที่นั่นแล้วคุณจะเห็นว่ามีการใช้โอเวอร์โหลดที่ผู้ใช้กำหนดในทั้งสองกรณี แน่นอนว่าสามารถสร้างความแตกต่างได้เล็กน้อยหากคุณตรวจสอบaก่อนหน้านี้bแล้วสลับตำแหน่งaจากตัวถูกดำเนินการด้านขวาเป็นตัวถูกดำเนินการด้านซ้าย แต่คุณสามารถตรวจสอบbก่อนหน้าaนี้ได้จากนั้นb == nullจะเป็นลำดับที่กลับรายการ ทั้งหมดนี้เป็นความสับสนที่มีผู้ให้บริการเรียกตัวเอง ให้โยนตัวถูกดำเนินการอย่างใดอย่างหนึ่ง (หรือทั้งสองอย่าง) เพื่อobjectให้ได้โอเวอร์โหลดที่ต้องการ กดไลค์if ((object)a == null)หรือif (a == (object)null).
Jeppe Stig Nielsen

10

เช่นเดียวกับที่ทุกคนตั้งข้อสังเกตว่ามันมาจากภาษา C ไม่มากก็น้อยซึ่งคุณอาจได้รับรหัสเท็จหากคุณลืมเครื่องหมายเท่ากับที่สองโดยไม่ได้ตั้งใจ แต่ยังมีอีกเหตุผลหนึ่งที่ตรงกับ C #: ความสามารถในการอ่าน

เพียงแค่ใช้ตัวอย่างง่ายๆนี้:

if(someVariableThatShouldBeChecked != null
   && anotherOne != null
   && justAnotherCheckThatIsNeededForTestingNullity != null
   && allTheseChecksAreReallyBoring != null
   && thereSeemsToBeADesignFlawIfSoManyChecksAreNeeded != null)
{
    // ToDo: Everything is checked, do something...
}

หากคุณเพียงแค่สลับคำว่างทั้งหมดไปยังจุดเริ่มต้นคุณสามารถตรวจสอบการตรวจสอบทั้งหมดได้ง่ายขึ้น:

if(null != someVariableThatShouldBeChecked
   && null != anotherOne
   && null != justAnotherCheckThatIsNeededForTestingNullity
   && null != allTheseChecksAreReallyBoring
   && null != thereSeemsToBeADesignFlawIfSoManyChecksAreNeeded)
{
    // ToDo: Everything is checked, do something...
}

ดังนั้นตัวอย่างนี้อาจเป็นตัวอย่างที่ไม่ดี (ดูหลักเกณฑ์การเข้ารหัส) แต่ลองนึกถึงคุณเลื่อนดูไฟล์โค้ดทั้งหมดอย่างรวดเร็ว เพียงแค่เห็นรูปแบบ

if(null ...

คุณรู้ทันทีว่าจะเกิดอะไรขึ้นต่อไป

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


9

ฉันเดาว่านี่เป็นโปรแกรมเมอร์ C ที่เปลี่ยนภาษาแล้ว

ใน C คุณสามารถเขียนสิ่งต่อไปนี้:

int i = 0;
if (i = 1)
{
    ...
}

สังเกตการใช้เครื่องหมายเท่ากับเครื่องหมายเดียวที่นั่นซึ่งหมายความว่ารหัสจะกำหนด 1 ให้กับตัวแปร i จากนั้นส่งกลับ 1 (การกำหนดคือนิพจน์) และใช้ 1 ในคำสั่ง if ซึ่งจะถูกจัดการว่าเป็นจริง กล่าวอีกนัยหนึ่งข้างต้นเป็นข้อบกพร่อง

อย่างไรก็ตามใน C # ไม่สามารถทำได้ แน่นอนไม่มีความแตกต่างระหว่างทั้งสอง


1
"ใน C # อย่างไรก็ตามสิ่งนี้เป็นไปไม่ได้" หากไม่มีคอมไพเลอร์บ่นเนื่องจากคำสั่ง if ต้องการนิพจน์บูลีนและคำสั่งที่ระบุเป็นประเภท int
Robert Harvey

4

ในสมัยก่อนผู้คนจะลืมคำว่า "!" (หรือค่าพิเศษ '=' เพื่อความเท่าเทียมซึ่งยากต่อการมองเห็น) และทำการมอบหมายแทนการเปรียบเทียบ การวาง null ไว้ข้างหน้าจะช่วยลดความเป็นไปได้ของจุดบกพร่องเนื่องจาก null ไม่ใช่ค่า l (IE ไม่สามารถกำหนดให้ได้)

คอมไพเลอร์สมัยใหม่ส่วนใหญ่จะแจ้งเตือนเมื่อคุณทำงานในเงื่อนไขปัจจุบันและ C # ให้ข้อผิดพลาด คนส่วนใหญ่ยึดติดกับรูปแบบ var == null เนื่องจากบางคนอ่านง่ายกว่า


คอมไพเลอร์ C # ทั้งหมด (โปรดทราบว่านี่คือคำถาม C #) ไม่ควรรวบรวมคำสั่ง "if" โดยที่นิพจน์ไม่ใช่บูลีนแม้ว่า ...
Jon Skeet

4

ฉันไม่เห็นประโยชน์ใด ๆ ในการปฏิบัติตามอนุสัญญานี้ ใน C ซึ่งไม่มีประเภทบูลีนการเขียนจะมีประโยชน์

if( 5 == variable)

ค่อนข้างมากกว่า

if (variable == 5)

เพราะถ้าคุณลืมเครื่องหมาย eaqual อันใดอันหนึ่งคุณจะจบลงด้วย

if (variable = 5)

ซึ่งกำหนด 5 ให้กับตัวแปรและประเมินเป็นจริงเสมอ แต่ใน Java บูลีนคือบูลีน และด้วย! = ไม่มีเหตุผลเลย

แต่คำแนะนำที่ดีอย่างหนึ่งคือการเขียน

if (CONSTANT.equals(myString))

ค่อนข้างมากกว่า

if (myString.equals(CONSTANT))

เพราะช่วยหลีกเลี่ยง NullPointerExceptions

คำแนะนำของฉันคือการขอเหตุผลของกฎ ถ้าไม่มีจะติดตามทำไม มันไม่ได้ช่วยในการอ่าน


0

สำหรับฉันแล้วคุณมักจะชอบสไตล์ไหน

@Shy - จากนั้นอีกครั้งหากคุณสับสนกับโอเปอเรเตอร์คุณควรต้องการรับข้อผิดพลาดในการคอมไพล์หรือคุณจะรันโค้ดด้วยบั๊กซึ่งเป็นข้อผิดพลาดที่จะกลับมากัดคุณในภายหลังเนื่องจากมันสร้างพฤติกรรมที่ไม่คาดคิด


ฉันไม่คิดว่าฉันเคยได้ยินใครพอใจ "คง == ตัวแปร" ในรูปแบบอื่น ๆกว่าสำหรับเหตุผลของความปลอดภัยซึ่งจะได้รับไพ่แล้วกับใน C #
Jon Skeet

ฉันชอบ "ตัวแปร == ค่าคงที่" ด้วยตัวเอง แต่ฉันเคยเห็นโปรแกรมเมอร์ใช้การย้อนกลับเพราะพวกเขารู้สึกว่ามันเป็นธรรมชาติมากกว่าสำหรับพวกเขา
TheCodeJunkie

1
พวกเขาทั้งหมดเป็นคนที่มี C หลายปีล้างสมองอยู่เบื้องหลังพวกเขาหรือเป็นทางเลือกที่แท้จริง?
Jon Skeet

0

ดังที่หลายคนชี้ให้เห็นส่วนใหญ่เป็นรหัส C เก่าที่ใช้เพื่อระบุข้อผิดพลาดในการคอมไพเลอร์เนื่องจากคอมไพเลอร์ยอมรับว่าเป็นกฎหมาย

ภาษาโปรแกรมใหม่เช่น java go นั้นฉลาดพอที่จะจับข้อผิดพลาดในการคอมไพล์ดังกล่าว

ไม่ควรใช้ "null! = variable" เหมือนเงื่อนไขในโค้ดเนื่องจากอ่านไม่ได้มาก


-4

อีกอย่างหนึ่ง ... หากคุณกำลังเปรียบเทียบตัวแปรกับค่าคงที่ (จำนวนเต็มหรือสตริงสำหรับอดีต) การใส่ค่าคงที่ทางซ้ายถือเป็นแนวทางปฏิบัติที่ดีเพราะคุณจะไม่มีทางเจอ NullPointerExceptions:

int i;
if(i==1){        // Exception raised: i is not initialized. (C/C++)
  doThis();
}

ในขณะที่

int i;
if(1==i){        // OK, but the condition is not met.
  doThis();
}

ตอนนี้เนื่องจากโดยค่าเริ่มต้น C # จะติดตั้งตัวแปรทั้งหมดคุณจึงไม่ควรมีปัญหาในภาษานั้น


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

แน่นอนทั้งสองรหัสจะรวบรวม! นั่นคือปัญหา ลองรันโค้ดแรกแล้วคุณจะเข้าใจว่า "Exception Raise" หมายถึงอะไร ...
koni

ก็ไม่ได้รับความแตกต่างระหว่างทั้งสอง คุณสามารถอธิบายได้หรือไม่?
mfazekas

ไม่มีข้อยกเว้นเว้นแต่คุณจะประกาศ int * i ใน C / C ++ และแม้ในกรณีนั้นจะเปรียบเทียบพอยน์เตอร์และไม่ทิ้งข้อยกเว้นใด ๆ ... เช่นเดียวกับ Dolphin ระบุว่าค่าไม่ได้กำหนด แต่จะไม่สร้างข้อยกเว้นใด ๆ
Cobusve

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