ควรให้บริการมีข้อยกเว้นหรือส่งคืนเมื่อไม่มีรายการที่ระบุไว้สำหรับการลบ


12

ฉันมีโค้ดบางส่วนที่สามารถแสดงเป็น:

public class ItemService {

    public void DeleteItems(IEnumerable<Item> items)
    {
        // Save us from possible NullReferenceException below.
        if(items == null)
            return;

        foreach(var item in items)
        {
            // For the purpose of this example, lets say I have to iterate over them.
            // Go to database and delete them.
        }
    }
}

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

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


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


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

2
เป็นไปได้ไหมที่จำนวนที่ว่างเปล่าแทนที่จะเป็นค่าว่าง? คุณจะจัดการกับสิ่งนั้นหรือไม่?
JAD

13
@Falgantil ฉันสามารถดูเป็นโมฆะสมควรยกเว้น แต่ฉันคิดว่ามันไร้สาระที่จะโยนข้อยกเว้นในรายการเปล่า
เควิน

คำตอบ:


58

นี่เป็นคำถามสองข้อที่แตกต่างกัน

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

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


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

2
... คุณควรจะได้รับการยกเว้นทันทีเนื่องจากพารามิเตอร์ที่เป็นร่างหากคุณรู้ว่าสิ่งที่คุณกำลังทำอยู่นั้นเป็นโมฆะในบางกรณีที่คาดหวังและคุณต้องการให้ฟังก์ชันที่คุณโทรหานั้นตรวจสอบให้คุณ เช่นเดียวกับ Kilian ที่กล่าวไว้ในคำตอบนโยบายทั่วไปของคุณอาจถูกแบนเป็นโมฆะทุกที่ที่ไม่ได้รับอนุญาตอย่างชัดเจน ถ้าอย่างนั้นคุณจะไม่จับNullReferenceExceptionหรือAhaINoticedYouPassingInNullExceptionที่ไหนก็ได้ยกเว้นรหัสการกู้คืนความเสียหายระดับสูง และจัดการข้อยกเว้นอาจจะสร้างรายงานข้อผิดพลาด :-)
สตีฟเจสซอพ

2
@FCin: การออกแบบส่วนต่อประสานของคุณควรตรงกับสิ่งที่ผู้ใช้ต้องการจากบริการ ดังนั้นหากผู้โทรทุกคนจะลอง / ลองดูวิธีนี้หรือวิธีการอำนวยความสะดวกอื่น ๆ ควรตรวจสอบและไม่สนใจ null เพราะนั่นคือสิ่งที่คุณต้องการ อย่างไรก็ตามดังที่ Kilian กล่าวว่าโดยทั่วไปแล้วจะทำงานได้ดีกว่าในการจัดการ nulls ที่เผยแพร่เป็นข้อผิดพลาดในการเขียนโปรแกรมและไม่พยายามดำเนินการต่อในทุกไซต์การโทร สำหรับเรื่องนั้นจะเกิดอะไรขึ้นถ้าบริการที่เรียกวิธีนี้ว่าnull- ตัวควบคุมมีแผงควบคุมสำเร็จรูปเพื่อจับและดำเนินการต่อในกรณีนั้นด้วยหรือไม่
Steve Jessop

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

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

9

ค่า Null

ดังที่ @KilianFoth ได้กล่าวไปแล้วโปรดทำตามนโยบายทั่วไปของคุณ หากนั่นคือการถือว่าnullเป็น "ชวเลข" สำหรับรายการว่างทำอย่างนั้น

หากคุณไม่มีนโยบายที่สอดคล้องกับnullค่าฉันขอแนะนำนโยบายต่อไปนี้:

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

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

รายการว่างเปล่า

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

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


1

โยนข้อยกเว้นและจัดการ nulls ในรหัสโทร

ตามกฎการออกแบบพยายามหลีกเลี่ยงค่า null เป็นค่าพารามิเตอร์ มันจะลด NullPointerExceptions โดยทั่วไปเนื่องจากค่า Null จะเป็นข้อยกเว้นจริงๆ

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


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

0

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

คุณสามารถใช้กระบวนการคิดนั้นกับสถานการณ์นี้ ระบุว่านี่เป็นคลาสสาธารณะที่มีวิธีการสาธารณะคุณมี API สาธารณะและในทางทฤษฎีไม่สามารถควบคุมสิ่งที่ผ่านไปได้

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


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

1
@FCin เนื่องจากคอลเล็กชันว่างคุณรู้ว่ามันว่างเปล่า กับnullคุณไม่มีการเก็บ หากรหัสการโทรควร / คาดว่าจะให้การรวบรวมกับวิธีการมีสิ่งผิดปกติดังนั้นคุณควรโยน เหตุผลเดียวในการถือ null เป็นเช่นเดียวกับคอลเลกชันที่ว่างเปล่าคือถ้าคุณสามารถคาดหวังว่ารหัสการโทรในการผลิตคอลเลกชันโมฆะ ตามที่คำตอบอื่น ๆ ระบุไว้ส่วนใหญ่สิ่งที่nullเป็นความผิดปกติ หากคุณปฏิบัติต่อสิ่งเหล่านี้เหมือนกับคอลเลกชันที่ว่างเปล่าคุณอาจเพิกเฉยต่อปัญหาอื่น ๆ ในรหัส
JAD

@ FCin: ถ้าคุณใช้เวลาnullว่างเปล่านั่นเป็นบริบทเฉพาะของวิธีนี้ ที่อื่นในฐานรหัสเดียวกันคุณอาจมีIEnumerableหรือIContainerพารามิเตอร์ที่เรียงลำดับการกรองบางอย่างnullอาจหมายถึง "ไม่มีตัวกรอง" (อนุญาตทุกอย่าง) ในขณะที่ตัวกรองที่ว่างเปล่าหมายถึงไม่มีอะไรเลย และในที่อื่นnullอาจหมายถึง "ฉันไม่รู้" อย่างชัดเจน ดังนั้นเนื่องจากสิ่งเหล่าnullนี้ไม่ได้หมายความว่าจะเหมือนกันทุกที่ทุกเวลาจึงเป็นความคิดที่ดีที่จะไม่สร้างความหมายใด ๆ เลยเว้นแต่ว่าความหมายนั้นมีความจำเป็นหรืออย่างน้อยก็มีประโยชน์กับใครบางคน
Steve Jessop

0

คำถามนี้ดูเหมือนจะไม่เกี่ยวกับข้อยกเว้น แต่nullเป็นอาร์กิวเมนต์ที่ถูกต้อง

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

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

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

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

ฉันอาจทำผิดด้านการอนุญาตnullค่ากำหนดและจัดทำเอกสารความหมายของพวกเขา (เช่นnull = empty arrayในกรณีนี้) และไม่ส่งข้อยกเว้นเว้นแต่ 99% ของรหัสที่คล้ายกันอื่น ๆ ของคุณ (สไตล์ "ไลบรารี่") ทำแบบอื่น 'รอบ.


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

@ SteveJessop ฉันไม่รู้จริง ๆ ว่าคุณกำลังเถียงกับวิญญาณของคำตอบของฉันหรือไม่ว่าคุณจะดำเนินการต่อไปตามสายของมัน ที่กล่าวว่าฉันไม่ได้แนะนำให้กลืนง่ายnullๆ แต่ต้องประกาศอย่างเปิดเผยในสัญญาของวิธีการที่ยอมรับได้ (หรือไม่ขึ้นอยู่กับวิธีแก้ไขปัญหาใด ๆ ที่ OP เกิดขึ้น) และจากนั้นคำถามจะทิ้งข้อยกเว้น (หรือไม่) แก้ไขตัวเอง
AnoE
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.