เหตุใดจึงมีความแตกต่างในการตรวจสอบค่า null เทียบกับค่าใน VB.NET และ C #


110

ในVB.NETสิ่งนี้เกิดขึ้น:

Dim x As System.Nullable(Of Decimal) = Nothing
Dim y As System.Nullable(Of Decimal) = Nothing

y = 5
If x <> y Then
    Console.WriteLine("true")
Else
    Console.WriteLine("false") '' <-- I got this. Why?
End If

แต่ใน C # สิ่งนี้เกิดขึ้น:

decimal? x = default(decimal?);
decimal? y = default(decimal?);

y = 5;
if (x != y)
{
    Debug.WriteLine("true"); // <-- I got this -- I'm with you, C# :)
}
else
{
    Debug.WriteLine("false");
}

ทำไมถึงมีความแตกต่าง?


22
ที่น่ากลัว
Mikeb

8
ผมเชื่อว่าdefault(decimal?)จะกลับ 0 nullไม่
Ryan Frame

7
@RyanFrame NO. เนื่องจากเป็นประเภท nullableจึงส่งคืนnull
Soner Gönül

4
ใช่แล้ว ... ใช่แล้ว ... ในIfเงื่อนไขVB ไม่จำเป็นต้องประเมินเป็นบูลีน ... uuuugh EDIT: Nothing <> Anything = Nothingซึ่งส่งผลให้Ifใช้เส้นทางลบ / อื่น
Chris Sinclair

13
@JMK: Null, Nothing และ Empty นั้นแตกต่างกันอย่างละเอียด ถ้าพวกเขาเหมือนกันทั้งหมดคุณก็ไม่จำเป็นต้องมีสามคน
Eric Lippert

คำตอบ:


88

VB.NET และ C # .NET เป็นภาษาที่แตกต่างกันซึ่งสร้างขึ้นโดยทีมงานต่างๆที่ตั้งสมมติฐานเกี่ยวกับการใช้งานที่แตกต่างกัน ในกรณีนี้คือความหมายของการเปรียบเทียบค่า NULL

ความชอบส่วนตัวของฉันคือความหมายของ VB.NET ซึ่งโดยพื้นฐานแล้วให้ NULL เป็นความหมาย "ฉันยังไม่รู้" จากนั้นเปรียบเทียบ 5 กับ "ฉันยังไม่รู้" เป็นธรรมชาติ "ฉันยังไม่รู้"; เช่น NULL สิ่งนี้มีข้อดีเพิ่มเติมของการจำลองลักษณะการทำงานของ NULL ในฐานข้อมูล SQL (ส่วนใหญ่ถ้าไม่ใช่ทั้งหมด) นี้ยังเป็นมาตรฐานมากขึ้น (กว่า C # 's) การตีความของตรรกะสามมูลค่าตามที่อธิบายไว้ที่นี่

ทีม C # ตั้งสมมติฐานที่แตกต่างกันเกี่ยวกับความหมายของ NULL ซึ่งส่งผลให้พฤติกรรมที่คุณแสดงนั้นแตกต่างกัน เอริค Lippert เขียนบล็อกเกี่ยวกับความหมายของการเป็นโมฆะใน C # Per Eric Lippert: "ฉันยังเขียนเกี่ยวกับความหมายของ null ใน VB / VBScript และ JScript ที่นี่และ ที่นี่ "

ในสภาพแวดล้อมใด ๆ ที่เป็นไปได้ค่า NULL เป็นเรื่องที่ไม่จำเป็นที่จะต้องยอมรับว่ากฎแห่งการยกเว้นกลาง (กล่าวคือ A หรือ ~ A เป็นความจริงอย่างเคร่งครัด) ไม่สามารถพึ่งพาได้อีกต่อไป

อัปเดต:

A bool(ตรงข้ามกับ a bool?) สามารถรับได้เฉพาะค่า TRUE และ FALSE อย่างไรก็ตามการใช้ภาษาของ NULL ต้องตัดสินใจว่า NULL แพร่กระจายผ่านนิพจน์อย่างไร ใน VB นิพจน์5=nullและ5<>nullทั้งสองส่งคืนเท็จ ใน C # ของนิพจน์ที่เทียบเคียงกัน5==nullและ5!=nullเฉพาะวินาทีแรก[อัปเดตเมื่อ 2014-03-02 - PG] เท่านั้นที่คืนค่าเป็นเท็จ อย่างไรก็ตามในสภาพแวดล้อมใด ๆ ที่สนับสนุน null โปรแกรมเมอร์จะต้องทราบตารางความจริงและการแพร่กระจาย null ที่ใช้โดยภาษานั้นในสภาพแวดล้อมใด ๆ

อัปเดต

บทความบล็อกของ Eric Lippert (กล่าวถึงในความคิดเห็นด้านล่าง) เกี่ยวกับความหมายอยู่ที่:


4
ขอบคุณสำหรับลิงค์ ฉันยังเขียนเกี่ยวกับความหมายของ null ใน VB / VBScript และ JScript ที่นี่: blogs.msdn.com/b/ericlippert/archive/2003/09/30/53120.aspx และที่นี่: blogs.msdn.com/b/ericlippert/ archive / 2003/10/01 / 53128.aspx
Eric Lippert

27
และ FYI การตัดสินใจให้ C # ไม่เข้ากันกับ VB ด้วยวิธีนี้เป็นเรื่องที่ขัดแย้งกัน ฉันไม่ได้อยู่ในทีมออกแบบภาษาในเวลานั้น แต่มีการถกเถียงกันมากมายในการตัดสินใจครั้งนี้
Eric Lippert

2
@ BlueRaja-DannyPflughoeft ใน C # boolไม่สามารถมี 3 ค่าได้มีเพียงสองค่า มันbool?สามารถมีสามค่า operator ==และoperator !=ทั้งสองกลับมาboolไม่bool?คำนึงถึงประเภทของตัวถูกดำเนินการ นอกจากนี้ifคำสั่งสามารถยอมรับได้เท่านั้นboolไม่ใช่bool?.
Servy

1
ใน C # นิพจน์5=nullและ5<>nullไม่ถูกต้อง และ5 == nullและ5 != nullคุณแน่ใจว่ามันเป็นครั้งที่สองที่ผลตอบแทนfalse?
Ben Voigt

1
@BenVoigt: ขอบคุณครับ คะแนนโหวตทั้งหมดเหล่านี้และคุณเป็นคนแรกที่สังเกตเห็นการพิมพ์ผิด ;-)
Pieter Geerkens

37

เพราะx <> yผลตอบแทนNothingแทนtrue. ไม่ได้กำหนดไว้เนื่องจากxไม่ได้กำหนดไว้ (คล้ายกับ SQL null)

หมายเหตุ: VB.NET Nothing<> C # null.

คุณต้องเปรียบเทียบค่าของ a Nullable(Of Decimal)ก็ต่อเมื่อมีค่า

ดังนั้น VB.NET ด้านบนจึงเปรียบเทียบคล้ายกับสิ่งนี้ (ซึ่งดูไม่ถูกต้องน้อยกว่า):

If x.HasValue AndAlso y.HasValue AndAlso x <> y Then
    Console.WriteLine("true")
Else
    Console.WriteLine("false")  
End If

ข้อกำหนดภาษา VB.NET :

7.1.1 ประเภทค่า Nullable ... ชนิดของค่าที่เป็นโมฆะสามารถมีค่าเดียวกันกับประเภทที่ไม่เป็นโมฆะได้เช่นเดียวกับค่า null ดังนั้นสำหรับประเภทค่าที่เป็นโมฆะการกำหนด Nothing ให้กับตัวแปรชนิดจะตั้งค่าของตัวแปรเป็นค่า null ไม่ใช่ค่าศูนย์ของประเภทค่า

ตัวอย่างเช่น:

Dim x As Integer = Nothing
Dim y As Integer? = Nothing

Console.WriteLine(x) ' Prints zero '
Console.WriteLine(y) ' Prints nothing (because the value of y is the null value) '

16
"VB.NET Nothing <> C # null" ส่งคืนจริงสำหรับ C # และเท็จสำหรับ VB.Net หรือไม่ ล้อเล่นครับ :-p
ken2k

17

ดูCIL ที่สร้างขึ้น(ฉันแปลงทั้งคู่เป็น C #):

ค#:

private static void Main(string[] args)
{
    decimal? x = null;
    decimal? y = null;
    y = 5M;
    decimal? CS$0$0000 = x;
    decimal? CS$0$0001 = y;
    if ((CS$0$0000.GetValueOrDefault() != CS$0$0001.GetValueOrDefault()) ||
        (CS$0$0000.HasValue != CS$0$0001.HasValue))
    {
        Console.WriteLine("true");
    }
    else
    {
        Console.WriteLine("false");
    }
}

Visual Basic:

[STAThread]
public static void Main()
{
    decimal? x = null;
    decimal? y = null;
    y = 5M;
    bool? VB$LW$t_struct$S3 = new bool?(decimal.Compare(x.GetValueOrDefault(), y.GetValueOrDefault()) != 0);
    bool? VB$LW$t_struct$S1 = (x.HasValue & y.HasValue) ? VB$LW$t_struct$S3 : null;
    if (VB$LW$t_struct$S1.GetValueOrDefault())
    {
        Console.WriteLine("true");
    }
    else
    {
        Console.WriteLine("false");
    }
}

คุณจะเห็นว่าการเปรียบเทียบใน Visual Basic ส่งคืนค่า Nullable <bool> (ไม่ใช่บูลเท็จหรือจริง!) และการแปลงที่ไม่ได้กำหนดเป็นบูลนั้นเป็นเท็จ

Nothingเมื่อเทียบกับสิ่งที่เป็นเสมอNothingไม่ใช่เท็จใน Visual Basic (เหมือนกับใน SQL)


ทำไมต้องตอบคำถามด้วยการลองผิดลองถูก? ควรจะทำได้จากข้อกำหนดภาษา
David Heffernan

3
@DavidHeffernan เพราะสิ่งนี้แสดงให้เห็นถึงความแตกต่างของภาษาที่ค่อนข้างชัดเจน
nothrow

2
@ Yossarian คุณคิดว่าข้อกำหนดของภาษามีความคลุมเครือในประเด็นนี้ ฉันไม่เห็นด้วย. IL เป็นรายละเอียดการดำเนินการอาจมีการเปลี่ยนแปลง รายละเอียดไม่ได้
Servy

2
@DavidHeffernan: ฉันชอบทัศนคติของคุณและแนะนำให้คุณลอง ข้อกำหนดภาษา VB อาจเป็นเรื่องยากที่จะแยกวิเคราะห์ในบางครั้ง Lucian ได้ปรับปรุงมาเป็นเวลาหลายปีแล้ว แต่ก็ยังค่อนข้างยากที่จะแยกแยะความหมายที่แท้จริงของกรณีมุมประเภทนี้ ฉันขอแนะนำให้คุณขอรับสำเนาข้อมูลจำเพาะทำการวิจัยและรายงานสิ่งที่คุณค้นพบ
Eric Lippert

2
@Yossarian ผลการดำเนินรหัส IL คุณได้ให้ไว้ไม่อยู่ภายใต้การเปลี่ยนแปลง แต่ที่รหัส C # / VB ให้ไว้จะถูกเรียบเรียงรหัส IL คุณแสดงให้เห็นว่าเป็นอาจมีการเปลี่ยนแปลง (ตราบเท่าที่พฤติกรรมของ IL เป็น ยังสอดคล้องกับข้อกำหนดของข้อกำหนดภาษา)
Servy

6

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

ในความเป็นจริงสาเหตุพื้นฐานของความสับสนคือความเชื่อที่เข้าใจผิดว่ารูปแบบต่างๆของความเท่าเทียมกันและการทดสอบความไม่เท่าเทียมกันควรได้รับผลเช่นเดียวกันแม้ว่าความจริงที่ว่าความหมายที่แตกต่างกันจะมีประโยชน์ในสถานการณ์ที่แตกต่างกัน ตัวอย่างเช่นจากมุมมองทางคณิตศาสตร์จะมีประโยชน์ที่จะสามารถมีDecimalสิ่งที่แตกต่างกันเฉพาะในจำนวนของศูนย์ต่อท้ายที่เปรียบเทียบได้เท่ากัน ในทำนองเดียวกันสำหรับdoubleค่าเช่นศูนย์บวกและศูนย์ลบ ในทางกลับกันจากมุมมองการแคชหรือการฝึกงานความหมายดังกล่าวอาจเป็นอันตรายถึงตายได้ ตัวอย่างเช่นสมมติหนึ่งมีDictionary<Decimal, String>ดังกล่าวว่าควรจะเท่ากับmyDict[someDecimal] someDecimal.ToString()วัตถุดังกล่าวจะดูสมเหตุสมผลถ้ามีหลายชิ้นDecimalค่าที่ต้องการแปลงเป็นสตริงและคาดว่าจะมีรายการซ้ำกันมาก น่าเสียดายหากใช้การแคชดังกล่าวเพื่อแปลง 12.3 ม. และ 12.40 ม. ตามด้วย 12.30 ม. และ 12.4 ม. ค่าหลังจะให้ผลเป็น "12.3" และ "12.40" แทนที่จะเป็น "12.30" และ "12.4"

ย้อนกลับไปที่เรื่องในมือมีมากกว่าหนึ่งวิธีที่สมเหตุสมผลในการเปรียบเทียบวัตถุที่เป็นโมฆะเพื่อความเท่าเทียมกัน C # จะใช้เวลาจุดยืนว่าผู้ประกอบการควรจะสะท้อนพฤติกรรมของ== VB.NETมีจุดยืนว่าพฤติกรรมของมันควรสะท้อนให้เห็นถึงภาษาอื่นเนื่องจากใครก็ตามที่ต้องการพฤติกรรมสามารถใช้ได้ ในบางแง่วิธีการแก้ปัญหาที่ถูกต้องคือการสร้าง "if" สามทางและกำหนดว่าหากนิพจน์เงื่อนไขส่งคืนผลลัพธ์ที่มีค่าสามค่าโค้ดจะต้องระบุสิ่งที่ควรเกิดขึ้นในกรณีนี้ เนื่องจากนั่นไม่ใช่ตัวเลือกที่มีภาษาอย่างที่เป็นอยู่ทางเลือกถัดไปที่ดีที่สุดคือเพียงแค่เรียนรู้ว่าภาษาต่างๆทำงานอย่างไรและรับรู้ว่าภาษาเหล่านั้นไม่เหมือนกันEqualsEqualsEqualsnull

อนึ่งโอเปอเรเตอร์ "Is" ของ Visual Basic ซึ่งไม่มีใน C สามารถใช้เพื่อทดสอบว่าออบเจ็กต์ที่เป็นโมฆะนั้นเป็นโมฆะหรือไม่ ในขณะที่บางคนอาจตั้งคำถามอย่างสมเหตุสมผลว่าการifทดสอบควรยอมรับ a Boolean?หรือไม่ แต่การให้ตัวดำเนินการเปรียบเทียบปกติกลับมาBoolean?แทนที่จะBooleanเรียกใช้ในประเภท nullable เป็นคุณลักษณะที่มีประโยชน์ อนึ่งใน VB.NET หากมีผู้พยายามใช้ตัวดำเนินการความเท่าเทียมกันมากกว่าIsหนึ่งจะได้รับคำเตือนว่าผลลัพธ์ของการเปรียบเทียบจะเป็นเสมอNothingและควรใช้Isหากต้องการทดสอบว่ามีบางสิ่งเป็นโมฆะหรือไม่


การทดสอบว่าคลาสเป็นโมฆะใน C # ทำได้== nullหรือไม่ และการทดสอบว่าประเภทค่าที่เป็นโมฆะมีค่านั้นทำได้.hasValueหรือไม่ สิ่งที่ใช้จะมีการIs Nothingดำเนินการ? C # มีisแต่จะทดสอบความเข้ากันได้ของประเภท ในแง่นี้ฉันไม่แน่ใจจริงๆว่าย่อหน้าสุดท้ายของคุณพยายามจะพูดอะไร
ErikE

@ErikE: ทั้ง vb.net และ C # อนุญาตให้ตรวจสอบประเภทที่เป็นโมฆะได้สำหรับค่าโดยใช้การเปรียบเทียบnullแม้ว่าทั้งสองภาษาจะถือว่าเป็นน้ำตาลเชิงไวยากรณ์สำหรับการHasValueตรวจสอบอย่างน้อยในกรณีที่ทราบประเภท (ฉันไม่แน่ใจ รหัสอะไรถูกสร้างขึ้นสำหรับยาชื่อสามัญ)
supercat

ในยาชื่อสามัญคุณจะได้รับปัญหาที่ยุ่งยากเกี่ยวกับประเภทที่เป็น
โมฆะ

3

โพสต์นี้อาจ ช่วยคุณได้:

ถ้าจำไม่ผิด "Nothing" ใน VB หมายถึง "ค่าเริ่มต้น" สำหรับประเภทค่านั่นคือค่าเริ่มต้นสำหรับประเภทการอ้างอิงซึ่งจะเป็นค่าว่าง ดังนั้นการกำหนดอะไรให้กับโครงสร้างจึงไม่มีปัญหาเลย


3
สิ่งนี้ไม่ตอบคำถาม
David Heffernan

ไม่มันชี้แจงอะไรเลย คำถามคือทั้งหมดเกี่ยวกับตัว<>ดำเนินการใน VB และวิธีการทำงานกับประเภท nullable
David Heffernan

2

นี่คือความแปลกประหลาดของ VB

ใน VB ถ้าคุณต้องการเปรียบเทียบสองประเภท nullable Nullable.Equals()คุณควรใช้

ในตัวอย่างของคุณควรเป็น:

Dim x As System.Nullable(Of Decimal) = Nothing
Dim y As System.Nullable(Of Decimal) = Nothing

y = 5
If Not Nullable.Equals(x, y) Then
    Console.WriteLine("true")
Else
    Console.WriteLine("false")
End If

5
มันเป็น "ความแปลก" เมื่อไม่คุ้นเคย ดูคำตอบของ Pieter Geerkens
rskar

ฉันก็คิดว่ามันแปลกที่ VB ไม่สร้างพฤติกรรมของNullable<>.Equals(). อาจมีคนคาดหวังว่ามันจะทำงานในลักษณะเดียวกัน (ซึ่งก็คือสิ่งที่ C # ทำ)
Matthew Watson

ความคาดหวังเช่นเดียวกับสิ่งที่ "ใคร ๆ ก็คาดหวัง" นั้นเกี่ยวกับสิ่งที่คน ๆ หนึ่งประสบมา C # ได้รับการออกแบบโดยคำนึงถึงความคาดหวังของผู้ใช้ Java Java ได้รับการออกแบบโดยคำนึงถึงความคาดหวังของผู้ใช้ C / C ++ เพื่อให้ดีขึ้นหรือแย่ลง VB.NET ได้รับการออกแบบโดยคำนึงถึงความคาดหวังของผู้ใช้ VB6 อาหารเสริมความคิดเพิ่มเติมได้ที่stackoverflow.com/questions/14837209/…และstackoverflow.com/questions/10176737/…
rskar

1
@MatthewWatson คำจำกัดความของNullableไม่มีอยู่ใน. NET เวอร์ชันแรกมันถูกสร้างขึ้นหลังจาก C # และ VB.NET หยุดทำงานไประยะหนึ่งและได้กำหนดพฤติกรรมการแพร่กระจายแบบว่างเปล่าแล้ว คุณคาดหวังโดยสุจริตว่าภาษาจะสอดคล้องกับประเภทที่ไม่ได้ถูกสร้างขึ้นมาเป็นเวลาหลายปีหรือไม่? จากมุมมองของโปรแกรมเมอร์ VB.NET มันเป็น Nullable เสมอกันที่ไม่สอดคล้องกับภาษาแทนที่จะเป็นอย่างอื่น (เนื่องจาก C # และ VB ใช้Nullableคำจำกัดความเดียวกันจึงไม่มีทางที่จะสอดคล้องกับทั้งสองภาษาได้)
Servy

0

รหัส VB ​​ของคุณไม่ถูกต้อง - หากคุณเปลี่ยน "x <> y" เป็น "x = y" ผลลัพธ์จะยังคงมี "เท็จ" วิธีที่ใช้บ่อยที่สุดในการแสดงออกสำหรับอินสแตนซ์ที่เป็นโมฆะคือ "ไม่ใช่ x.Equals (y)" และจะให้ลักษณะการทำงานเหมือนกับ "x! = y" ใน C #


1
เว้นแต่จะxเป็นnothingในกรณีที่x.Equals(y)จะโยนข้อยกเว้น
Servy

@Servy: สะดุดกับสิ่งนี้อีกครั้ง (หลายปีต่อมา) และสังเกตว่าฉันไม่ได้แก้ไขคุณ - "x.Equals (y)" จะไม่ทิ้งข้อยกเว้นสำหรับอินสแตนซ์ประเภท 'x' ที่เป็นโมฆะ ประเภท Nullable ได้รับการปฏิบัติที่แตกต่างกันโดยคอมไพเลอร์
Dave Doknjas

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