มันไม่มีเหตุผลที่จะคาดหวังใด ๆ () * ไม่ * ที่จะโยนข้อยกเว้นอ้างอิงเป็นโมฆะ?


41

เมื่อคุณสร้างวิธีการขยายที่คุณสามารถทำได้แน่นอนว่าเรียกใช้บนnullแต่ไม่เหมือนกับการเรียกใช้วิธีการแบบอินสแตนซ์การเรียกใช้แบบ null ไม่จำเป็นต้องมีเครื่องหมายNullReferenceException-> คุณต้องตรวจสอบและโยนด้วยตนเอง

สำหรับการดำเนินการตามวิธีส่วนขยายของ Linq Any()Microsoft ตัดสินใจว่าพวกเขาควรจะโยนArgumentNullException( https://github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/Linq/AnyAll.cs )

มันทำให้ฉันต้องเขียน if( myCollection != null && myCollection.Any() )

ฉันผิดในฐานะลูกค้าของรหัสนี้ที่จะคาดหวังว่าเช่น((int[])null).Any()ควรกลับมาfalse?


40
ใน C # 6 คุณสามารถทำให้การตรวจสอบของคุณง่ายขึ้นหาก (myCollection? .Any () == จริง)
17 จาก 26

5
แม้จะอยู่ใน F # ซึ่งไม่ได้จริงๆใช้ nulls ยกเว้นสำหรับการทำงานร่วมกันกับ NET ภาษาอื่น ๆพ่นnull |> Seq.isEmpty System.ArgumentNullException: Value cannot be nullความคาดหวังดูเหมือนว่าคุณจะไม่ผ่านค่าที่ไม่ได้กำหนดสำหรับบางสิ่งที่คาดว่าจะมีอยู่ดังนั้นจึงเป็นข้อยกเว้นเมื่อคุณมีค่าว่าง nullถ้ามันเป็นเรื่องของการเริ่มต้นที่ผมจะเริ่มต้นด้วยลำดับที่ว่างเปล่าแทน
Aaron M. Eshbach

21
นั่นเป็นสาเหตุที่คุณไม่ควรกลับมาnullเมื่อจัดการกับคอลเลกชัน แต่ควรใช้คอลเล็กชันที่ว่างเปล่าแทน
Bakuriu

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

22
ฉันควรชี้ให้เห็นว่าทุกวิธี LINQ เดียวโยนอาร์กิวเมนต์ที่เป็นโมฆะ Anyมีความสอดคล้องกัน
เซบาสเตียนเรดล

คำตอบ:


157

ฉันมีถุงที่มีมันฝรั่งห้าตัวอยู่ข้างใน มี.Any()มันฝรั่งอยู่ในถุงไหม?

" ใช่ " คุณพูด<= true

ฉันนำมันฝรั่งออกมาแล้วกิน มี.Any()มันฝรั่งอยู่ในถุงไหม?

" ไม่ " คุณพูด<= false

ฉันเผาเตาให้เป็นกอง .Any()ตอนนี้มีมันฝรั่งอยู่ในถุงไหม?

" ไม่มีกระเป๋า "<= ArgumentNullException


3
แต่ไม่มีถุงในมันฝรั่งเลย? เท็จหมายถึงเท็จ ... (ฉันไม่เห็นด้วยเพียงแค่มุมมองที่สอง)
D. Ben Knoble

6
ยิ่งกว่านั้นบางคนส่งกล่องปิดให้คุณจากแหล่งที่ไม่รู้จัก ถุงในกล่องบรรจุมันฝรั่งหรือไม่? ฉันสนใจเพียง 2 สถานการณ์ - ก) มีถุงในกล่องที่มีมันฝรั่งอยู่หรือ B) ไม่มีมันฝรั่งและ / หรือไม่มีถุงอยู่ในกล่อง ความหมายใช่มีความแตกต่างระหว่าง null และว่างเปล่า แต่ 99% ของเวลาที่ฉันไม่สนใจ
เดฟ thieben

6
คุณกินมันฝรั่งดิบห้าเม็ด ?? รีบไปพบแพทย์ทันที
Oly

3
@Oly Dan ปรุงสุกด้วยไฟก่อนรับประทานอาหารและใช้ไฟในการเผาถุง
Ismael Miguel

3
@ D.BenKnoble: โดยที่ไม่ได้เห็นลำตัวรถของฉันคุณยินดีที่จะเป็นพยานว่าไม่มีศพอยู่ในท้ายรถของฉันเหรอ? ไม่มี? ความแตกต่างระหว่างการตรวจสอบลำตัวและไม่พบสิ่งใดหรือไม่ตรวจสอบลำตัว? ตอนนี้คุณไม่สามารถเป็นพยานได้เพราะคุณจะได้เห็นร่างกายในปริมาณเท่ากันไม่ว่าในกรณีใด? ... นั่นคือความแตกต่าง คุณไม่สามารถรับรองอะไรได้ถ้าคุณไม่สามารถยืนยันได้ในตอนแรก คุณคิดว่าทั้งสองนั้นเท่ากัน แต่นั่นไม่ใช่ของที่กำหนด ในบางกรณีอาจมีความแตกต่างที่สำคัญระหว่างทั้งสอง
Flater

52

ปิดแรกก็ปรากฏว่ารหัสที่มาที่จะโยนไม่ArgumentNullExceptionNullReferenceException

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

ฉันผิดในฐานะลูกค้าของรหัสนี้ที่จะคาดหวังว่าเช่น((int[])null).Any()ควรกลับมาfalse?

ใช่. คำถามที่Any()คำตอบคือ "การรวบรวมนี้มีองค์ประกอบใด ๆ ?" หากไม่มีคอลเล็กชันนี้แสดงว่าคำถามนั้นไร้สาระ มันไม่สามารถมีหรือไม่มีสิ่งใดเลยเพราะมันไม่มีอยู่


18
@ChrisWohlert: ปรีชาของคุณสนใจฉัน คุณคิดด้วยว่าความหมายคนที่ไม่มีตัวตนเป็นคนที่ว่างเปล่าซึ่งชื่อสตริงและอายุที่ว่างเปล่าเป็นศูนย์และเป็นเช่นนั้นหรือไม่ และคุณคิดว่าชุดที่มีnullนั้นเทียบเท่ากับชุดที่มีชุดเปล่า (และในทางกลับกัน)?
ruakh

17
@ChrisWohlert: เห็นได้ชัดว่าชุดสามารถมีชุดที่ว่างเปล่า; แต่คุณดูเหมือนจะเชื่ออย่างนั้น{ null }และ{ {} }ควรจะเท่าเทียมกัน ฉันพบว่าน่าหลงใหล ฉันไม่สามารถเกี่ยวข้องกับมันเลย
ruakh

9
.Addในnullคอลเล็กชั่นควรสร้างคอลเลคชั่นสำหรับเราด้วยหรือไม่?
แมทธิว

13
@ChrisWohlert "A เป็นชุดที่ว่างเปล่า B เป็นชุดที่มีแตงโมเป็นที่ว่างเปล่าใช่ใช่ B ว่างหรือไม่ไม่ C ว่างหรือไม่ Uhhh ... "
253751

16
จุดอวดความคิด: ในกรณีของทฤษฎีเซตชุดที่ไม่มีอยู่เป็นชุดเปล่า มีชุดว่างอยู่และชุดที่ไม่ได้มีอยู่นั้นไม่ใช่ชุดนั้น (และแน่นอนไม่มีอะไรเลยเนื่องจากไม่มีอยู่)
James_pic

22

Null หมายถึงข้อมูลที่ขาดหายไปไม่ใช่องค์ประกอบ

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

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

ดูเพิ่มเติมที่ https://stackoverflow.com/questions/1191919/what-does-linq-return-when-the-results-are-empty


1
นั่นnullไม่ได้หมายความว่าองค์ประกอบที่ไม่เป็นเรื่องของการใช้การประชุมสมเหตุสมผล แค่คิดเกี่ยวกับ OLESTRINGS พื้นเมืองที่มันจริงไม่หมายถึงการที่
Deduplicator

1
@Deduplicator คุณกำลังพูดถึงการเชื่อมโยงออบเจกต์และการฝังของ Microsoft หรือไม่ ถ้าคุณจะจำได้ว่า OLE มาจากยุค 90! ในปัจจุบันมีภาษาที่ทันสมัยหลายภาษา (C #, Java, Swift) ที่ให้สิ่งอำนวยความสะดวกในการกำจัด / กำจัดการใช้ null
Erik Eidt

2
@ErikEidt พวกเขากำลังทำเช่นนั้นส่วนหนึ่งnullไม่ได้หมายความว่า "ขาดข้อมูล" nullไม่มีการตีความที่มีความหมายเดียว แต่เป็นเรื่องของวิธีที่คุณปฏิบัติต่อมัน nullบางครั้งใช้สำหรับ "ข้อมูลที่ขาดหายไป" แต่สำหรับ "ไม่มีองค์ประกอบ" และสำหรับ "ข้อผิดพลาดเกิดขึ้น" หรือ "ไม่กำหนดค่าเริ่มต้น" หรือ "ข้อมูลที่ขัดแย้งกัน" หรือบางส่วนตามอำเภอใจ
Derek Elkins

-1 นี่เป็นการตัดสินใจของนักพัฒนาไม่ใช่ทฤษฎีที่เป็นนามธรรม nullมีการใช้บ่อยครั้งเพื่อหมายถึง "ไม่มีข้อมูล" บางครั้งมันก็สมเหตุสมผล ครั้งอื่นไม่
jpmc26

13

นอกเหนือจากไวยากรณ์ null เงื่อนไขnullมีเทคนิคที่จะบรรเทาปัญหานี้อีกจะไม่ปล่อยให้ตัวแปรของคุณเคยยังคงอยู่

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

public MyResult DoSomething(int a, IEnumerable<string> words)
{
    words = words ?? Enumerable.Empty<string>();

    if (!words.Any())
    {
        ...

คุณสามารถทำเช่นเดียวกันเมื่อดึงข้อมูลคอลเลกชันจากวิธีอื่น:

var words = GetWords() ?? Enumerable.Empty<string>();

(โปรดทราบว่าในกรณีที่คุณสามารถควบคุมฟังก์ชั่นที่ต้องการGetWordsและnullเทียบเท่ากับคอลเลกชันที่ว่างเปล่าคุณควรกลับไปที่คอลเล็กชั่นที่ว่างเปล่าในตอนแรก)

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


12

ฉันผิดฉันเป็นลูกค้าของรหัสนี้เพื่อคาดหวังว่าเช่น ((int []) null) .Any () ควรกลับเท็จ?

ใช่เพียงเพราะคุณอยู่ใน C # และพฤติกรรมนั้นได้รับการกำหนดและบันทึกไว้อย่างดี

หากคุณสร้างห้องสมุดของคุณเองหรือถ้าคุณใช้ภาษาที่แตกต่างกับวัฒนธรรมการยกเว้นที่แตกต่างกันมันจะสมเหตุสมผลกว่าที่จะคาดหวังว่าเท็จ

โดยส่วนตัวแล้วฉันรู้สึกว่าการส่งคืนเท็จเป็นวิธีที่ปลอดภัยกว่าที่ทำให้โปรแกรมของคุณแข็งแกร่งขึ้น แต่ก็เป็นที่ถกเถียงกันอย่างน้อย


15
'ความแข็งแกร่ง' นั้นมักจะเป็นภาพลวงตา - หากรหัสที่สร้างอาร์เรย์เริ่มสร้าง NULL โดยไม่คาดคิดเนื่องจากข้อผิดพลาดหรือปัญหาอื่น ๆ รหัส 'แข็งแกร่ง' ของคุณจะซ่อนปัญหา มันเป็นพฤติกรรมที่เหมาะสมในบางเวลา แต่ไม่ใช่ค่าเริ่มต้นที่ปลอดภัย
GoatInTheMachine

1
@ GoatInTheMachine - ฉันไม่เห็นด้วยเกี่ยวกับเรื่องนี้ว่าเป็นค่าเริ่มต้นที่ปลอดภัย แต่คุณก็ถูกต้องที่จะซ่อนปัญหาและพฤติกรรมดังกล่าวไม่เป็นที่ต้องการในบางครั้ง จากประสบการณ์ของฉันการซ่อนปัญหา (หรือการเชื่อถือการทดสอบหน่วยเพื่อตรวจจับปัญหาของรหัสอื่น) ดีกว่าการหยุดแอพของคุณโดยการโยนข้อยกเว้นที่ไม่คาดคิด ฉันหมายถึงจริงๆ - ถ้ารหัสโทรเสียพอที่จะส่งเป็นโมฆะโอกาสที่พวกเขาจัดการข้อยกเว้นอย่างถูกต้องคืออะไร?
Telastyn

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

1
@GoInInMachine - ฉันเห็นด้วยกับสิ่งต่าง ๆ มากมาย แต่คอลเลกชันมีแนวโน้มที่จะแตกต่างกัน การใช้ null เป็นคอลเล็กชั่นที่ว่างเปล่านั้นไม่ใช่พฤติกรรมที่ไม่สอดคล้องหรือน่าประหลาดใจอย่างมาก
Telastyn

8
ลองนึกภาพคุณมีอาร์เรย์ที่มีประชากรจากเอกสาร JSON ที่ได้รับจากคำขอทางเว็บและในการผลิตหนึ่งในลูกค้าของ API ของคุณก็มีปัญหาที่พวกเขาส่งผ่าน JSON ที่ไม่ถูกต้องบางส่วนทำให้คุณต้องยกเลิกอาร์เรย์เป็น NULL คุณต้องการ: รหัสที่เลวร้ายที่สุดโยนข้อยกเว้นการอ้างอิงเป็นโมฆะในขณะที่คำขอไม่ดีครั้งแรกเข้ามาซึ่งคุณจะเห็นในการเข้าสู่ระบบแล้วแจ้งให้ลูกค้าทราบทันทีเพื่อแก้ไขคำขอที่ไม่ทำงานหรือรหัสของคุณบอกว่า "โอ้อาร์เรย์ว่างเปล่า ทำ!" และเขียน 'ตะกร้าซื้อไม่มีรายการ' ในฐานข้อมูลเป็นเวลาสองสามสัปดาห์
GoatInTheMachine

10

หากการตรวจสอบโมฆะซ้ำ ๆ ทำให้คุณรำคาญคุณสามารถสร้างวิธีการขยาย 'IsNullOrEmpty ()' ของคุณเองเพื่อทำมิเรอร์เมธอด String ด้วยชื่อนั้นและตัดทั้งการตรวจสอบค่า null และการเรียก. ANny () เข้าเป็นการโทรครั้งเดียว

มิฉะนั้นโซลูชันดังกล่าวที่ @ 17 จาก 26 ในความคิดเห็นภายใต้คำถามของคุณจะสั้นกว่าวิธี 'มาตรฐาน' และชัดเจนสำหรับผู้ที่คุ้นเคยกับไวยากรณ์เงื่อนไขแบบโมฆะใหม่

if(myCollection?.Any() == true)

4
นอกจากนี้คุณยังสามารถใช้แทน?? false == trueนี่จะดูเหมือนชัดเจนกว่า (สะอาดกว่า) สำหรับฉันเพราะมันจัดการเฉพาะกรณีของnullและมันก็ไม่ได้ verbose อีกต่อไป การ==ตรวจสอบเพิ่มเติมสำหรับ Booleans มักจะกระตุ้นการปิดปากของฉัน =)
jpmc26

2
@ jpmc26: ฉันไม่รู้เกี่ยวกับ C # มากนัก ทำไมยังไม่myCollection?.Any()พอ
Eric Duminil

6
@EricDuminil เนื่องจากวิธีการทำงานของโอเปอเรเตอร์ที่มีเงื่อนไขmyCollection?.Any()จะส่งคืนบูลีนที่มีค่าเป็นโมฆะได้อย่างมีประสิทธิภาพแทนที่จะเป็นบูลีนธรรมดา (นั่นคือบูล? แทนที่จะเป็นบูล) ไม่มีการแปลงโดยนัยจากบูล เพื่อบูลและมันเป็นบูลที่เราต้องการในตัวอย่างนี้ (เพื่อทดสอบโดยเป็นส่วนหนึ่งของifเงื่อนไข) ดังนั้นเราต้องเปรียบเทียบอย่างชัดเจนกับtrueหรือใช้ประโยชน์จากตัวดำเนินการ null-coalescing (??)
Steven Rands

@ jpmc26 ?? false อ่านยากกว่า myCollection? .Any () == จริง โดยไม่คำนึงถึงการปิดปากสะท้อนของคุณ == จริงคือสิ่งที่คุณพยายามทดสอบโดยปริยาย ?? เท็จเพียงแค่เพิ่มเสียงเพิ่มเติมและคุณยังคงต้องทำประเมินผลในหัวของคุณเกี่ยวกับสิ่งที่เกิดขึ้นถ้า null, null ว่า == ?.จริงจะล้มเหลวเกือบจะไม่มีผู้อ่านไม่ต้องมีการตระหนักถึง
Ryan The Leach

2
@ RyanTheLeach ฉันต้องคิดมากขึ้นว่า==จะใช้งานได้จริงหรือไม่ ฉันรู้แล้วว่าเกิดอะไรขึ้นกับบูลีนอย่างสังหรณ์ใจเมื่อมันสามารถเป็นtrueหรือfalse; ฉันไม่ต้องคิดอะไร แต่โยนnullลงไปผสมและตอนนี้ฉันต้องทำงานออกว่า==ถูกต้อง ??แทนที่จะเป็นเพียงแค่ช่วยลดnullกรณีลดมันกลับไปtrueและfalseที่คุณแล้วเข้าใจทันที สำหรับการสะท้อนปิดปากของฉันเมื่อฉันเห็นมันครั้งแรกสัญชาตญาณในทันทีของฉันคือการลบมันเพราะมันมักจะไร้ประโยชน์ซึ่งคุณไม่ต้องการเกิดขึ้น
jpmc26

8

ฉันผิดฉันเป็นลูกค้าของรหัสนี้เพื่อคาดหวังว่าเช่น ((int []) null) .Any () ควรกลับเท็จ?

หากคุณสงสัยเกี่ยวกับความคาดหวังคุณต้องคิดถึงความตั้งใจ

null หมายถึงสิ่งที่แตกต่างจาก Enumerable.Empty<T>

ตามที่ระบุไว้ในคำตอบของ Erik Eidtมีความแตกต่างในความหมายระหว่างnullและคอลเลกชันที่ว่างเปล่า

เรามาดูกันว่าพวกเขาควรจะใช้งานอย่างไร

แนวทางการออกแบบเฟรมเวิร์กของหนังสือ: อนุสัญญาสำนวนและรูปแบบสำหรับไลบรารี. NET ที่ใช้ซ้ำได้รุ่นที่ 2 ที่เขียนโดยสถาปนิกของ Microsoft Krzysztof CwalinaและBrad Abramsระบุแนวทางปฏิบัติที่ดีที่สุดดังต่อไปนี้:

X ไม่ส่งคืนค่า Null จากคุณสมบัติการรวบรวมหรือจากวิธีการคืนค่าการรวบรวม ส่งคืนคอลเล็กชันว่างหรืออาร์เรย์ว่างแทน

พิจารณาของคุณเรียกวิธีการที่ในที่สุดได้รับข้อมูลจากฐานข้อมูล: ถ้าคุณได้รับอาร์เรย์ที่ว่างเปล่าหรือEnumerable.Empty<T>นี้ก็หมายความว่าตัวอย่างพื้นที่ว่างเปล่าคือผลของคุณเป็นเซตว่าง ที่ได้รับnullในบริบทนี้อย่างไรจะมีความหมายสภาวะข้อผิดพลาด

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


1
ไม่ว่าพวกเขาจะมีความหมายที่แตกต่างกันขึ้นอยู่กับบริบทหรือไม่ บ่อยครั้งเพื่อจุดประสงค์ของตรรกะอะไรก็ตามที่อยู่ในมือพวกเขาแสดงถึงแนวคิดที่เทียบเท่า ไม่เสมอไป แต่บ่อยครั้ง
jpmc26

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

@Chris และฉันกำลังบอกว่าแนวทางการเข้ารหัสจากผู้ที่ออกแบบภาษานั้นไม่สามารถใช้แทนการประเมินโค้ดของคุณในบริบทของความต้องการของคุณไลบรารีที่คุณใช้และนักพัฒนาคนอื่น ๆ ที่คุณทำงานด้วย ความคิดที่ว่าพวกเขาจะไม่เท่าเทียมกันในบริบทคืออุดมการณ์พายในท้องฟ้า พวกเขาอาจไม่เทียบเท่ากับข้อมูลในมือโดยทั่วไป แต่จะเทียบเท่ากับการสร้างผลลัพธ์ในฟังก์ชันเฉพาะบางอย่างที่คุณเขียน ในกรณีนั้นคุณต้องสนับสนุนทั้งคู่ แต่ต้องแน่ใจว่าพวกเขาสร้างผลลัพธ์เดียวกัน
jpmc26

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

@ Chris ฉันหมายถึงมีบริบทที่แตกต่างกัน (ซึ่งมักจะสอดคล้องกับขอบเขต) พิจารณาฟังก์ชั่นเช่น nullและที่ว่างเปล่าอาจส่งผลให้มีค่าตอบแทนที่เหมือนกันสำหรับฟังก์ชั่น แต่นั่นไม่ได้หมายความว่าnullและที่ว่างเปล่าหมายถึงสิ่งเดียวกันในขอบเขตการโทร
jpmc26

3

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

ฉันผิดฉันเป็นลูกค้าของรหัสนี้เพื่อคาดหวังว่าเช่น ((int []) null) .Any () ควรกลับเท็จ?

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

ระบุว่าAny()หากไม่มีภาคแสดงCount() > 0แล้วคุณคาดหวังอะไรจากตัวอย่างนี้

List<int> list = null;
if (list.Count > 0) {}

หรือทั่วไป:

List<int> list = null;
if (list.Foo()) {}

NullReferenceExceptionฉันคิดว่าคุณคาดหวัง

  • Any()เป็นวิธีการขยายมันควรรวมกับวัตถุที่ขยายอย่างราบรื่นแล้วโยนข้อยกเว้นเป็นสิ่งที่น่าแปลกใจน้อยที่สุด
  • ไม่ใช่ทุกภาษา. NET ที่รองรับวิธีการขยาย
  • คุณสามารถเรียกEnumerable.Any(null)และมีArgumentNullExceptionแน่นอนคุณคาดหวัง มันเป็นวิธีการเดียวกันและจะต้องสอดคล้องกับ - อาจ - เกือบทุกอย่างอื่นในกรอบ
  • การเข้าถึงnullวัตถุเป็นข้อผิดพลาดของการเขียนโปรแกรมกรอบไม่ควรบังคับใช้null เป็นค่ามายากล หากคุณใช้วิธีการนี้คุณต้องรับผิดชอบในการจัดการกับมัน
  • มันมีความเห็นคุณคิดอย่างหนึ่งและฉันก็คิดอย่างอื่น กรอบงานควรไม่ถูก จำกัด เท่าที่จะทำได้
  • หากคุณมีกรณีพิเศษคุณจะต้องสอดคล้องกัน: คุณต้องทำการตัดสินใจที่ให้ความเห็นอย่างมากเกี่ยวกับวิธีการขยายอื่น ๆ ทั้งหมด: หากCount()ดูเหมือนเป็นการตัดสินใจที่ง่ายก็Where()ไม่ใช่ เกี่ยวกับMax()อะไร มันมีข้อยกเว้นสำหรับรายการที่ว่างเปล่ามันไม่ควรโยนสำหรับรายการnullหนึ่งหรือ

สิ่งที่นักออกแบบห้องสมุดได้ก่อน LINQ คือการแนะนำวิธีการอย่างชัดเจนเมื่อnullเป็นที่ถูกต้องคุ้มค่า (ตัวอย่างString.IsNullOrEmpty()) แล้วพวกเขาก็จะต้องมีความสอดคล้องกับปรัชญาการออกแบบที่มีอยู่ ที่กล่าวถึงแม้ว่าเล็กน้อยเขียนสองวิธีEmptyIfNull()และEmptyOrNull()อาจมีประโยชน์


1

จิมควรจะทิ้งมันฝรั่งไว้ในกระเป๋าทุกใบ มิฉะนั้นฉันจะฆ่าเขา

จิมมีถุงที่มีมันฝรั่งอยู่ห้าตัว มี.Any()มันฝรั่งอยู่ในถุงไหม?

"ใช่" คุณพูด <= จริง

โอเคจิมใช้ชีวิตในเวลานี้

จิมหยิบมันฝรั่งทั้งหมดออกมาแล้วกินมัน มีมันฝรั่งอยู่ในถุงไหม?

"ไม่" คุณพูด <= false

เวลาฆ่าจิม

จิมเผาถุงในกองไฟให้สนิท ตอนนี้มีมันฝรั่งอยู่ในถุงไหม?

"ไม่มีกระเป๋า" <= ArgumentNullException

จิมควรอยู่หรือตาย เราไม่ได้คาดหวังสิ่งนี้ดังนั้นฉันจึงต้องการการพิจารณาคดี ปล่อยให้จิมหนีปัญหานี้ไปหรือเปล่า?

คุณสามารถใช้คำอธิบายประกอบเพื่อส่งสัญญาณว่าคุณไม่ได้ทนกับคนที่ไม่มีเหตุผลใด ๆ ในลักษณะนี้

public bool Any( [NotNull] List bag ) 

แต่ห่วงโซ่เครื่องมือของคุณต้องรองรับ ซึ่งหมายความว่าคุณมีแนวโน้มที่จะยังคงสิ้นสุดการตรวจสอบการเขียน


0

หากสิ่งนี้รบกวนคุณมากฉันขอแนะนำวิธีการขยายอย่างง่าย

static public IEnumerable<T> NullToEmpty<T>(this IEnumerable<T> source)
{
    return (source == null) ? Enumerable.Empty<T>() : source;
}

ตอนนี้คุณสามารถทำได้:

List<string> list = null;
var flag = list.NullToEmpty().Any( s => s == "Foo" );

... falseและธงจะตั้งค่าให้


2
มีความเป็นธรรมชาติมากกว่าที่จะเขียนreturnข้อความเป็น:return source ?? Enumerable.Empty<T>();
Jeppe Stig Nielsen

สิ่งนี้ไม่ตอบคำถามที่ถาม
Kenneth K.

@ KennethK.but ดูเหมือนจะแก้ปัญหาที่นำเสนอ
RQDQ

0

นี่คือคำถามเกี่ยวกับวิธีการขยาย C # และปรัชญาการออกแบบของพวกเขาดังนั้นฉันคิดว่าวิธีที่ดีที่สุดในการตอบคำถามนี้คือการอ้างอิงเอกสารของ MSDN เกี่ยวกับวัตถุประสงค์ของวิธีการขยาย :

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

...

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

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

หากคุณใช้วิธีการขยายสำหรับประเภทที่กำหนดให้จำจุดต่อไปนี้:

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

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

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


* ขณะที่ทราบด้านตรงนี้เป็นสถานการณ์ที่นักออกแบบของ LINQ ที่ต้องเผชิญกับ: เมื่อ C # 3.0 ได้รับการปล่อยตัวมีอยู่นับล้านของลูกค้าที่ได้ใช้System.Collections.IEnumerableและSystem.Collections.Generic.IEnumerable<T>ทั้งในคอลเลกชันของพวกเขาและในforeachลูป ชั้นเรียนเหล่านี้กลับIEnumeratorวัตถุซึ่งมีเพียงสองวิธีCurrentและMoveNextเพื่อเพิ่มวิธีการเช่นใด ๆ เพิ่มเติมที่จำเป็นเช่นCount, Anyฯลฯ จะหมดล้านเหล่านี้ของลูกค้า ดังนั้นเพื่อให้ฟังก์ชั่นนี้ (โดยเฉพาะอย่างยิ่งเนื่องจากสามารถนำมาใช้ในแง่ของCurrentและMoveNextด้วยความสะดวกสัมพัทธ์) พวกเขาปล่อยมันเป็นวิธีการขยายซึ่งสามารถนำไปใช้กับที่มีอยู่ในปัจจุบันIEnumerableอินสแตนซ์และยังสามารถนำไปใช้กับคลาสได้ด้วยวิธีที่มีประสิทธิภาพมากขึ้น หากผู้ออกแบบของ C # ตัดสินใจที่จะเปิดตัว LINQ ในวันแรกมันจะถูกจัดเตรียมไว้เป็นวิธีการของอินสแตนซ์IEnumerableและพวกเขาอาจจะออกแบบระบบบางประเภท


0

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

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

การอ่านและการบำรุงรักษาสำคัญกว่า 'ฉันต้องเขียนเงื่อนไขพิเศษ' ทุกครั้ง


0

ตามกฎทั่วไปฉันเขียนรหัสของฉันส่วนใหญ่เพื่อสมมติว่าผู้โทรต้องรับผิดชอบในการไม่ส่งข้อมูลที่เป็นโมฆะให้ฉันส่วนใหญ่ด้วยเหตุผลที่กล่าวไว้ในSay No to Null (และโพสต์อื่น ๆ ที่คล้ายกัน) แนวคิดของ null มักไม่ค่อยเข้าใจและด้วยเหตุนี้จึงแนะนำให้เริ่มต้นตัวแปรของคุณล่วงหน้าก่อนการปฏิบัติเท่าที่ควร สมมติว่าคุณทำงานกับ API ที่เหมาะสมคุณไม่ควรได้รับค่า Null กลับคืนดังนั้นคุณไม่ควรตรวจสอบค่า Null จุดว่างตามคำตอบอื่น ๆ ที่ระบุไว้คือเพื่อให้แน่ใจว่าคุณมีวัตถุที่ถูกต้อง (เช่น "กระเป๋า") เพื่อทำงานกับก่อนดำเนินการต่อ นี่ไม่ใช่คุณสมบัติของAnyแต่เป็นคุณสมบัติของภาษาแทนตัวเอง คุณไม่สามารถทำการดำเนินการส่วนใหญ่บนวัตถุ null ได้เนื่องจากไม่มีอยู่ มันเหมือนกับว่าฉันขอให้คุณขับรถไปที่ร้านในรถของฉันยกเว้นฉันไม่ได้เป็นเจ้าของรถดังนั้นคุณไม่มีทางที่จะใช้รถของฉันขับรถไปที่ร้าน มันไร้สาระที่จะทำการผ่าตัดในสิ่งที่ไม่มีอยู่จริง

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

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