ฟังก์ชั่นควรกลับเป็นโมฆะหรือวัตถุที่ว่างเปล่า?


209

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

พิจารณาสิ่งนี้:

public UserEntity GetUserById(Guid userId)
{
     //Imagine some code here to access database.....

     //Check if data was returned and return a null if none found
     if (!DataExists)
        return null; 
        //Should I be doing this here instead? 
        //return new UserEntity();  
     else
        return existingUserEntity;
}

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


4
if (!DataExists)ฉันคิดว่าคุณหมายถึง
Sarah Vessels

107
นี่เป็นคำถามสถาปัตยกรรมและเหมาะสมอย่างสมบูรณ์ คำถามของ OP นั้นถูกต้องโดยไม่คำนึงถึงปัญหาทางธุรกิจที่พยายามแก้ไข
Joseph Ferris

2
คำถามนี้ได้รับคำตอบอย่างเพียงพอแล้ว ฉันคิดว่านี่เป็นคำถามที่น่าสนใจมาก
John Scipione

'getUser ()' ควรส่งคืนค่าว่าง 'getCurrentUserInfo ()' หรือ 'getCurrentPermissions ()', OTOH จะแสดงคำถามเพิ่มเติม - พวกเขาควรส่งคืนออบเจกต์คำตอบที่ไม่เป็นโมฆะโดยไม่คำนึงว่าบุคคลนั้นหรือใครก็ตามเข้าสู่ระบบ
โทมัส W

2
ไม่มี @Bergi อีกอันหนึ่งเป็นคู่ ฉันถูกถามก่อนในเดือนตุลาคมอีกคนถาม 3 ผีเสื้อต่อมาในเดือนธันวาคม อีกอย่างหนึ่งพูดถึงการสะสมซึ่งแตกต่างกันเล็กน้อย
7wp

คำตอบ:


207

การส่งคืนค่า Null เป็นความคิดที่ดีที่สุดหากคุณต้องการระบุว่าไม่มีข้อมูล

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

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


21
คุณควรจะโยนข้อยกเว้นไม่กลืนปัญหาและกลับมาเป็นโมฆะ อย่างน้อยคุณควรเข้าสู่ระบบและดำเนินการต่อ
Chris Ballance

130
@Chris: ฉันไม่เห็นด้วย หากรหัสเอกสารชัดเจนว่าค่าส่งคืนเป็นโมฆะมันเป็นที่ยอมรับได้อย่างสมบูรณ์ในการส่งคืน null หากไม่พบผลลัพธ์ที่ตรงกับเกณฑ์ของคุณ การโยนข้อยกเว้นควรเป็นทางเลือกสุดท้ายของคุณไม่ใช่ตัวเลือกแรกของคุณ
Mike Hofer

12
@Chris: คุณตัดสินใจเรื่องนี้บนพื้นฐานอะไร เพิ่มเข้าสู่ระบบสมการอย่างแน่นอนดูเหมือนว่ามากเกินไป ปล่อยให้รหัสการบริโภคตัดสินใจว่าควรทำอะไรในกรณีที่ไม่มีผู้ใช้ เช่นเดียวกับความคิดเห็นก่อนหน้าของฉันไม่มีปัญหากับการคืนค่าที่ถูกกำหนดโดยสากลว่า "ไม่มีข้อมูล"
Adam Robinson เมื่อ

17
ฉันรู้สึกสับสนเล็กน้อยที่ผู้พัฒนา Microsoft เชื่อว่า "การคืนค่าว่าง" เท่ากับ "กลืนปัญหา" หากหน่วยความจำทำหน้าที่มีเมธอดมากมายใน Framework ซึ่งเมธอด null ถูกส่งคืนหากไม่มีสิ่งใดที่ตรงกับคำขอของผู้โทร นั่นคือ "การกลืนปัญหาหรือไม่"
Mike Hofer

5
สุดท้าย แต่ไม่bool GetUserById(Guid userId, out UserEntity result)ท้ายสุด - ซึ่งฉันอยากจะคืนค่า "null" และสิ่งที่ไม่สุดขีดเท่ากับการโยนข้อยกเว้น จะช่วยให้สวยงามรหัสปราศจากเช่นnull if(GetUserById(x,u)) { ... }
Marcel Jackwerth

44

ขึ้นอยู่กับสิ่งที่เหมาะสมที่สุดสำหรับกรณีของคุณ

มันสมเหตุสมผลไหมที่จะส่งคืน null เช่น "ไม่มีผู้ใช้ดังกล่าว"

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

หรือเหมาะสมที่จะโยนข้อยกเว้น (a la "FileNotFound") หากรหัสการโทรกำลังเรียกร้องให้ผู้ใช้มี ID ที่ไม่ถูกต้อง

อย่างไรก็ตาม - จากการแยกข้อกังวล / มุมมอง SRP สองสิ่งแรกนั้นถูกต้องมากขึ้น และในทางเทคนิคแรกคือถูกต้องที่สุด (แต่โดยผมเท่านั้น) - GetUserById ควรรับผิดชอบสิ่งเดียว - รับผู้ใช้ การจัดการกรณี "ผู้ใช้ไม่มีอยู่" ของตนเองโดยการส่งคืนสิ่งอื่นอาจเป็นการละเมิด SRP แยกออกเป็นเช็คอื่น - bool DoesUserExist(id)จะเหมาะสมถ้าคุณเลือกที่จะโยนข้อยกเว้น

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

วิธีการทั้งหมดเป็นที่ยอมรับ - มันขึ้นอยู่กับบริบทที่ใหญ่กว่าของ API / แอปพลิเคชัน


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

(ฉันคาดเดา downvote คือการคัดค้านความคิดของการขว้างปายกเว้นในกรณีเช่นนี้.)
จาค็อบ Mattison

1
ตกลงกันจนถึงจุดสุดท้ายของคุณ ไม่มีการละเมิด SRP โดยส่งคืนค่าที่กำหนดแบบสากลว่า "ไม่มีข้อมูล" นั่นเหมือนกับการระบุว่าฐานข้อมูล SQL ควรส่งคืนข้อผิดพลาดหากข้อใดไม่มีผลลัพธ์ ในขณะที่การยกเว้นเป็นตัวเลือกการออกแบบที่ถูกต้อง (แม้ว่าจะทำให้ฉันรำคาญใจในฐานะผู้บริโภค) แต่ก็ไม่ได้ "ถูกต้อง" มากกว่าการส่งคืน null และไม่ฉันไม่ใช่ DV
Adam Robinson เมื่อ

@JacobM เราโยนข้อยกเว้นเมื่อเราต้องการเส้นทางของระบบไฟล์ที่ไม่มีอยู่ไม่คืนค่าว่าง แต่ไม่ใช่จากฐานข้อมูล เห็นได้ชัดว่าทั้งสองมีความเหมาะสมซึ่งเป็นสิ่งที่ฉันได้รับ - มันก็ขึ้นอยู่กับ
Rex M

2
@Charles: คุณกำลังตอบคำถาม "ควรจะมีข้อยกเว้นในบางจุด" แต่คำถามคือ "ฟังก์ชั่นนี้ควรจะโยนข้อยกเว้น" คำตอบที่ถูกต้องคือ "อาจจะ" ไม่ใช่ "ใช่"
Adam Robinson เมื่อ

30

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


เพิ่งจะเพิ่มนี่เป็นคำตอบด้วยตัวเอง NullObjectPattern หรือรูปแบบเคสพิเศษ จากนั้นคุณสามารถดำเนินการหนึ่งรายการสำหรับแต่ละกรณี NoUserEntitiesFound, NullUserEntities ฯลฯ
David Swindells

27

ถ้าประเภทการคืนของคุณเป็นอาร์เรย์ให้ส่งคืนอาร์เรย์ว่างเปล่ามิฉะนั้นคืนค่าเป็น null


0 รายการในรายการเหมือนกับรายการที่ไม่ได้ถูกกำหนดในเวลานี้หรือไม่?
AnthonyWJones

3
0 nullรายการในรายการไม่ได้เป็นเช่นเดียวกับ จะช่วยให้คุณที่จะใช้ในforeachงบและ LINQ NullReferenceExceptionคำสั่งโดยไม่ต้องกังวลสำหรับ
ดาริน Dimitrov

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

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

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

12

คุณควรโยนข้อยกเว้น (เท่านั้น) หากสัญญาที่เฉพาะเจาะจงเสีย
ในตัวอย่างเฉพาะของคุณการขอ UserEntity โดยอ้างอิงจาก Id ที่รู้จักมันจะขึ้นอยู่กับข้อเท็จจริงว่าผู้ใช้ที่หายไป (ถูกลบ) เป็นกรณีที่คาดหวังหรือไม่ หากเป็นเช่นนั้นให้ส่งคืนnullแต่ถ้าไม่ใช่กรณีที่คาดหวังให้ส่งข้อยกเว้น
โปรดทราบว่าหากมีการเรียกใช้ฟังก์ชั่นUserEntity GetUserByName(string name)มันอาจจะไม่โยน แต่คืนค่าว่าง ในทั้งสองกรณีที่ส่งคืน UserEntity ที่ว่างเปล่าจะไม่ช่วยเหลือ

สำหรับสตริงอาร์เรย์และคอลเลกชันสถานการณ์มักแตกต่างกัน ผมจำได้ว่าบาง MS รูปแบบแนวทางวิธีการที่ควรจะยอมรับnullเป็นรายการ 'ว่างเปล่า' คอลเลกชัน nullแต่การกลับมาของความยาวเป็นศูนย์มากกว่า เหมือนกันสำหรับสตริง โปรดทราบว่าคุณสามารถประกาศอาร์เรย์ที่ว่างเปล่า:int[] arr = new int[0];


ดีใจที่คุณพูดถึงสายต่าง ๆ เนื่องจาก Google แสดงให้ฉันดูเมื่อฉันตัดสินใจว่าจะส่งคืนสตริงว่างหรือไม่
Noumenon

สตริงคอลเลกชันและอาร์เรย์ไม่แตกต่างกัน ถ้า MS บอกว่า MS ผิด มีความแตกต่างระหว่างสตริงว่างและ null และระหว่างคอลเลกชันว่างและ null ในทั้งสองกรณีอดีตแสดงข้อมูลที่มีอยู่ (ขนาด 0) และหลังแสดงถึงการขาดข้อมูล ในบางกรณีความแตกต่างมีความสำคัญมาก ตัวอย่างเช่นหากคุณค้นหารายการในแคชคุณต้องการทราบความแตกต่างระหว่างข้อมูลที่ถูกแคช แต่ว่างเปล่าและข้อมูลไม่ถูกแคชดังนั้นคุณต้องดึงข้อมูลจากแหล่งข้อมูลพื้นฐานซึ่งอาจไม่ใช่ ว่างเปล่า
Reinstate Monica

1
คุณดูเหมือนจะพลาดจุดและบริบท ควรกลับคอลเลกชันที่ว่างเปล่าไม่ได้.Wher(p => p.Lastname == "qwerty") null
Henk Holterman

@HenkHolterman หากคุณสามารถเข้าถึงคอลเล็กชันเต็มรูปแบบและใช้ตัวกรองที่ยอมรับรายการในคอลเลกชันไม่ได้คอลเลกชันที่ว่างเปล่าคือผลลัพธ์ที่ถูกต้อง แต่ถ้าไม่มีคอลเลกชันเต็มคอลเลกชันที่ว่างเปล่าจะทำให้เกิดความเข้าใจผิดอย่างมาก - โมฆะหรือการโยนจะถูกต้องขึ้นอยู่กับว่าสถานการณ์นั้นเป็นเรื่องปกติหรือพิเศษ เนื่องจากโพสต์ของคุณไม่มีคุณสมบัติที่สถานการณ์ที่คุณกำลังพูดถึง (และตอนนี้คุณชี้แจงว่าคุณกำลังพูดถึงสถานการณ์เดิม) และเนื่องจาก OP กำลังพูดถึงสถานการณ์หลังฉันต้องไม่เห็นด้วยกับคุณ
Reinstate Monica

11

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

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

หากผู้ใช้ที่หายไปก็โอเค (หนึ่งในผลปกติที่เป็นไปได้ของการเรียกใช้ฟังก์ชันนี้) จากนั้นส่งคืน null

แก้ไข: (เพื่อตอบความคิดเห็นจากอดัมด้วยคำตอบอื่น)

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

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


-1 ถึงผู้ลงคะแนนเสียง +1 จาก Charles - เป็นคำถามทางธุรกิจทั้งหมดและไม่มีแนวปฏิบัติที่ดีที่สุดสำหรับเรื่องนี้
Austin Salonen

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

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

10

โดยส่วนตัวแล้วฉันจะคืนค่า null เพราะนั่นคือสิ่งที่ฉันคาดหวังว่าชั้น DAL / Repository จะทำหน้าที่

หากไม่มีอยู่อย่าส่งคืนสิ่งใดที่สามารถตีความได้ว่าการดึงวัตถุสำเร็จให้ใช้nullงานได้อย่างสวยงามที่นี่

สิ่งที่สำคัญที่สุดคือการทำให้ DAL / Repos Layer ของคุณสอดคล้องกันในแบบที่คุณไม่สับสนในการใช้งาน


7

ฉันมักจะ

  • return nullหากไม่มี ID วัตถุเมื่อไม่ทราบล่วงหน้าว่าควรมีอยู่หรือไม่
  • throwหาก id วัตถุไม่มีอยู่เมื่อควรจะมีอยู่

ฉันแยกความแตกต่างระหว่างสองสถานการณ์นี้ด้วยวิธีการสามประเภทนี้ ครั้งแรก:

Boolean TryGetSomeObjectById(Int32 id, out SomeObject o)
{
    if (InternalIdExists(id))
    {
        o = InternalGetSomeObject(id);

        return true;
    }
    else
    {
        return false;
    }
}

ประการที่สอง:

SomeObject FindSomeObjectById(Int32 id)
{
    SomeObject o;

    return TryGetObjectById(id, out o) ? o : null;
}

ที่สาม:

SomeObject GetSomeObjectById(Int32 id)
{
    SomeObject o;

    if (!TryGetObjectById(id, out o))
    {
        throw new SomeAppropriateException();
    }

    return o;
}

คุณหมายถึงoutไม่ref
แมตต์เอลเลน

@ แมท: ใช่แล้วฉันจะทำแน่นอนที่สุด! แก้ไขแล้ว.
Johann Gerell

2
นี่เป็นคำตอบเดียวที่เหมาะกับทุกคนดังนั้นจึงเป็นความจริงอย่างแท้จริง! :) ใช่มันขึ้นอยู่กับสมมติฐานตามวิธีการที่เรียกใช้ ... ดังนั้นก่อนอื่นให้ล้างสมมติฐานเหล่านี้แล้วเลือกชุดค่าผสมที่เหมาะสมจากด้านบน ต้องเลื่อนลงมากเกินไปเพื่อไปที่นี่ :) +100
yair

ดูเหมือนว่านี่จะเป็นรูปแบบที่จะใช้ยกเว้นว่าจะไม่รองรับวิธีการซิงค์ ฉันอ้างอิงคำตอบนี้และเพิ่มโซลูชัน async ด้วย Tuple Literals -> ที่นี่
ttugates

6

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

public void GetUserById(Guid id, UserCallback callback)
{
    // Lookup user
    if (userFound)
        callback(userEntity);  // or callback.Call(userEntity);
}

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

public void GetUserById(Guid id, UserCallback callback, NotFoundCallback notFound)
{
    // Lookup user
    if (userFound)
        callback(userEntity);  // or callback.Call(userEntity);
    else
        notFound(); // or notFound.Call();
}

วิธีการเดียวกันโดยใช้วัตถุเดียวอาจมีลักษณะดังนี้:

public void GetUserById(Guid id, UserCallback callback)
{
    // Lookup user
    if (userFound)
        callback.Found(userEntity);
    else
        callback.NotFound();
}

จากมุมมองของการออกแบบฉันชอบวิธีนี้มาก แต่มีข้อเสียเปรียบในการทำให้ไซต์โทรเป็นกลุ่มมากขึ้นในภาษาที่ไม่รองรับฟังก์ชั่นชั้นหนึ่งอย่างง่ายดาย


น่าสนใจ เมื่อคุณเริ่มพูดคุยเกี่ยวกับผู้ได้รับมอบหมายฉันเริ่มสงสัยทันทีว่าการแสดงออกของแลมบ์ดาสามารถใช้ที่นี่ได้หรือไม่
7wp

ได้! ตามที่ฉันเข้าใจแล้วไวยากรณ์แลมบ์ดาของ C # 3.0 และสูงกว่านั้นเป็นน้ำตาลเชิงประโยคสำหรับผู้ได้รับมอบหมายที่ไม่ระบุชื่อ ในทำนองเดียวกันใน Java โดยไม่มีแลมบ์ดาหรือไวยากรณ์ตัวแทนที่ไม่ระบุชื่อคุณสามารถสร้างคลาสที่ไม่ระบุชื่อได้ มันค่อนข้างน่าเกลียด แต่ก็มีประโยชน์จริงๆ ผมคิดว่าวันนี้, C # ตัวอย่างของฉันจะได้ใช้ Func <UserEntity> หรือสิ่งที่ชอบแทนการเป็นตัวแทนชื่อ แต่สุดท้ายโครงการ C # ผมอยู่บนยังคงใช้รุ่น 2
มาร์ค

+1 ฉันชอบวิธีนี้ แต่ปัญหาคือมันไม่ธรรมดาและเพิ่มอุปสรรคเล็กน้อยในการเข้าสู่ codebase
Timoxley

4

เราใช้ CSLA.NET และจะเห็นว่าการดึงข้อมูลที่ล้มเหลวควรส่งคืนวัตถุ "ว่าง" นี้เป็นจริงที่น่ารำคาญมากในขณะที่มันเรียกร้องการประชุมของการตรวจสอบว่าobj.IsNewrathern obj == nullกว่า

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

ส่วนตัวแล้วฉันคิดว่าnullสง่างามกว่า

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

คุณสามารถจัดการสิ่งนี้ได้โดย:

ถ้า (User.Exists (id)) {
  this.User = User.Fetch (id);
} อื่น {
  Response.Redirect ( "~ / notfound.aspx");
}

... แต่นั่นเป็นการโทรไปยังฐานข้อมูลเพิ่มเติมทุกครั้งซึ่งอาจเป็นปัญหาในหน้าเว็บที่มีการเข้าชมสูง โดย:

this.User = User.Fetch (id);

ถ้า (this.User == null) {
  Response.Redirect ( "~ / notfound.aspx");
}

... ต้องการเพียงหนึ่งสาย



4

ฉันจะบอกว่าคืนค่า null แทนวัตถุเปล่า

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

นี่เป็นกฎที่ฉันมักปฏิบัติตาม:

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

เหตุใดคุณต้องส่งข้อยกเว้นในกรณีเหล่านี้ บางครั้งผู้ใช้ไม่มีอยู่ในฐานข้อมูลและเราคาดหวังว่าอาจไม่เกิดขึ้น มันไม่ใช่พฤติกรรมที่ยอดเยี่ยม
siride

3

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

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


+1 ฉันถูกตั้งคำถามกับตรรกะเดียวกับที่คุณพูดที่นี่ซึ่งเป็นสาเหตุที่ฉันโพสต์คำถามเพื่อดูว่าคนอื่น ๆ มีความคิดเห็นอย่างไรเกี่ยวกับเรื่องนี้
7wp

3

ดีที่สุดในกรณีนี้คืนค่า "null" ในกรณีที่ไม่มีผู้ใช้ดังกล่าว ยังทำให้วิธีการของคุณคงที่

แก้ไข:

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


ฉันสามารถถามได้ไหมว่าทำไมฉันต้องการทำให้วิธีการของฉันคงที่? ถ้าฉันต้องการใช้การฉีดพึ่งพา
7wp

ตกลงตอนนี้ฉันได้รับตรรกะของคุณ แต่ฉันใช้รูปแบบ Repository และฉันชอบใช้การฉีดแบบพึ่งพาสำหรับที่เก็บของฉันดังนั้นฉันจึงไม่สามารถใช้วิธีแบบคงที่ได้ แต่ +1 สำหรับการแนะนำให้ส่งคืนโมฆะ :)
7wp

3

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

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


+1 ฉันชอบมุมมองทางเลือกของปัญหา ดังนั้นโดยพื้นฐานแล้วคุณกำลังพูดว่าวิธีใดที่ฉันเลือกควรจะดีตราบเท่าที่วิธีนั้นสอดคล้องกันตลอดทั้งแอปพลิเคชัน?
7wp

1
นั่นคือความเชื่อของฉัน ฉันคิดว่าความมั่นคงเป็นสิ่งสำคัญอย่างยิ่ง หากคุณทำสิ่งต่าง ๆ หลายวิธีในหลาย ๆ สถานที่นั้นจะมีความเสี่ยงสูงสำหรับข้อบกพร่องใหม่ เราได้ใช้วิธีการเริ่มต้นกับวัตถุเป็นการส่วนตัวเพราะมันทำงานได้ดีกับรูปแบบ Essence ที่เราใช้ในรูปแบบโดเมนของเรา เรามีวิธีการขยายทั่วไปแบบทั่วไปที่เราสามารถทดสอบกับวัตถุโดเมนทั้งหมดเพื่อบอกเราว่ามันมีการเติมข้อมูลหรือไม่เพื่อให้เรารู้ว่าสามารถทำการทดสอบใด ๆ ด้วยการเรียก objectname.IsDefault () - หลีกเลี่ยงการตรวจสอบความเท่าเทียมกันโดยตรง .
Joseph Ferris

3

ในวัตถุธุรกิจของเราเรามี 2 วิธีหลักในการรับ:

เพื่อให้สิ่งต่าง ๆ ง่ายขึ้นในบริบทหรือคุณถามพวกเขาจะเป็น:

// Returns null if user does not exist
public UserEntity GetUserById(Guid userId)
{
}

// Returns a New User if user does not exist
public UserEntity GetNewOrExistingUserById(Guid userId)
{
}

วิธีแรกจะใช้เมื่อรับเอนทิตีเฉพาะวิธีที่สองใช้เฉพาะเมื่อเพิ่มหรือแก้ไขเอนทิตีบนเว็บเพจ

สิ่งนี้ช่วยให้เรามีสิ่งที่ดีที่สุดของโลกทั้งสองในบริบทที่พวกเขาใช้


3

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

การใช้จาวาเราถูกขอให้เพิ่ม a assert exists(object) : "You shouldn't try to access an object that doesn't exist";ไว้ที่จุดเริ่มต้นของวิธีการใด ๆ ที่สามารถคืนค่า null เพื่อแสดง "เงื่อนไข" (ฉันไม่รู้ว่าคำศัพท์ภาษาอังกฤษคืออะไร)

IMO นี่ไม่ใช่เรื่องง่ายที่จะใช้ แต่นั่นคือสิ่งที่ฉันใช้กำลังรอสิ่งที่ดีกว่า


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

1
ประโยชน์อย่างหนึ่งคือการตรวจสอบการมีอยู่เป็นนามธรรมอย่างเหมาะสม: ถ้า (userExists) อ่านได้ง่ายขึ้นเล็กน้อยใกล้กับโดเมนปัญหา & 'คอมพิวเตอร์' น้อยกว่า: ถ้า (ผู้ใช้ == null)
timoxley

และฉันจะโต้แย้งว่า 'ถ้า (x == null)' เป็นรูปแบบเก่าแก่หลายสิบปีที่ถ้าคุณไม่เคยเห็นมาก่อนคุณยังไม่ได้เขียนโค้ดมานานมาก (และคุณควรคุ้นเคยกับมันเหมือนเดิม บรรทัดของรหัสนับล้าน) "Computery"? เรากำลังพูดถึงการเข้าถึงฐานข้อมูล ...
ลอยด์ซาร์เจนท์

3

หากกรณีที่ผู้ใช้ไม่ถูกพบขึ้นมามักจะเพียงพอและคุณต้องการที่จะจัดการกับว่าในรูปแบบต่างๆขึ้นอยู่กับสถานการณ์ (บางครั้งการขว้างปายกเว้นบางครั้งแทนผู้ใช้ที่ว่างเปล่า) คุณยังสามารถใช้สิ่งที่ใกล้เคียงกับ F # 's Optionหรือของ Haskell Maybeประเภท ซึ่งแยกกรณี 'ไม่มีค่า' อย่างชัดเจนจาก 'พบบางสิ่ง!' รหัสการเข้าถึงฐานข้อมูลอาจมีลักษณะเช่นนี้:

public Option<UserEntity> GetUserById(Guid userId)
{
 //Imagine some code here to access database.....

 //Check if data was returned and return a null if none found
 if (!DataExists)
    return Option<UserEntity>.Nothing; 
 else
    return Option.Just(existingUserEntity);
}

และนำมาใช้เช่นนี้:

Option<UserEntity> result = GetUserById(...);
if (result.IsNothing()) {
    // deal with it
} else {
    UserEntity value = result.GetValue();
}

น่าเสียดายที่ทุกคนดูเหมือนจะม้วนตัวเองแบบนี้


2

ฉันมักจะกลับมาเป็นโมฆะ มันมีกลไกที่ง่ายและรวดเร็วในการตรวจสอบว่ามีบางสิ่งผิดปกติเกิดขึ้นโดยไม่มีการโยนข้อยกเว้นและใช้การลอง / จับได้หลายจุด


2

สำหรับประเภทคอลเลกชันฉันจะคืนค่าคอลเลกชันที่ว่างเปล่าสำหรับประเภทอื่น ๆ ทั้งหมดที่ฉันต้องการใช้รูปแบบ NullObject สำหรับการส่งคืนวัตถุที่ใช้อินเทอร์เฟซเดียวกันกับชนิดที่กลับมา สำหรับรายละเอียดเกี่ยวกับรูปแบบตรวจสอบข้อความลิงค์

ใช้รูปแบบ NullObject ซึ่งจะเป็น: -

public UserEntity GetUserById(Guid userId)

{// ลองจินตนาการถึงโค้ดบางส่วนที่นี่เพื่อเข้าถึงฐานข้อมูล .....

 //Check if data was returned and return a null if none found
 if (!DataExists)
    return new NullUserEntity(); //Should I be doing this here instead? return new UserEntity();  
 else
    return existingUserEntity;

}

class NullUserEntity: IUserEntity { public string getFirstName(){ return ""; } ...} 

2

ที่จะนำสิ่งที่คนอื่นพูดในลักษณะเปี่ยมไปด้วยพลัง ...

ข้อยกเว้นสำหรับสถานการณ์พิเศษ

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

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

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

ตอนนี้ฉันจะบอกว่าเป็นกฎทั่วไป มีหลายครั้งที่คุณอาจต้องการทำลายมัน แต่ประสบการณ์และการทดลองของฉันด้วย C # (จำนวนมากนั้น) และ Java (บิตของนั้น) ได้สอนฉันว่ามันเป็นมากประสิทธิภาพมีราคาแพงกว่าที่ชาญฉลาดในการจัดการกับข้อยกเว้นกว่าที่จะจัดการกับปัญหาที่คาดเดาได้ผ่านทางตรรกะเงื่อนไข ฉันกำลังพูดถึงการปรับแต่งขนาด 2 หรือ 3 คำสั่งที่มีราคาแพงกว่าในบางกรณี ดังนั้นหากเป็นไปได้ว่ารหัสของคุณอาจจบลงด้วยการวนซ้ำฉันจะแนะนำให้คืนค่า Null และทำการทดสอบ


2

ให้อภัย pseudo-php / code ของฉัน

ฉันคิดว่ามันขึ้นอยู่กับการใช้ผลลัพธ์

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

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

ตัวอย่าง:

function saveTheRow($prim_key, $data) {
    $row = getRowByPrimKey($prim_key);

    // Populate the data here

    $row->save();
}

ที่นี่เราจะเห็นได้ว่าชุดการทำงานชุดเดียวกันจัดการระเบียนทั้งหมดของประเภทนี้

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

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

ตัวอย่าง:

function displayData($row_id) {
    // Logging of the error would happen in this function
    $row = getRow($row_id);
    if($row === null) {
        // Handle the error here
    }

    // Do stuff here with data
}

function getRow($row_id) {
 $row = null;
 try{
     if(!$db->connected()) {
   throw excpetion("Couldn't Connect");
  }

  $result = $db->query($some_query_using_row_id);

  if(count($result) == 0 ) {
   throw new exception("Couldn't find a record!");
  }

  $row = $db->nextRow();

 } catch (db_exception) {
  //Log db conn error, alert admin, etc...
  return null; // This way I know that null means an error occurred
 }
 return $row;
}

นั่นเป็นกฎทั่วไปของฉัน มันใช้งานได้ดีจนถึงตอนนี้


2

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

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


+1 ฉันชอบความคิดที่จะใช้รูปแบบการตั้งชื่อที่เหมือนกันสำหรับการส่งสัญญาณไปยังโปรแกรมเมอร์ว่าจะใช้ฟังก์ชันนั้นอย่างไร
7wp

1
ทันใดนั้นฉันก็รู้ว่านี่คือสิ่งที่ไม่ LINQ: พิจารณาครั้งแรก ( ... ) เทียบกับ FirstOrDefault ( ... )
มาร์ค Wittke

2

หากวัตถุที่ส่งคืนเป็นสิ่งที่สามารถทำซ้ำได้ฉันจะส่งคืนวัตถุเปล่าเพื่อที่ฉันจะได้ไม่ต้องทดสอบค่าว่างก่อน

ตัวอย่าง:

bool IsAdministrator(User user)
{
    var groupsOfUser = GetGroupsOfUser(user);

    // This foreach would cause a run time exception if groupsOfUser is null.
    foreach (var groupOfUser in groupsOfUser) 
    {
        if (groupOfUser.Name == "Administrators")
        {
            return true;
        }
    }

    return false;
}

2

ฉันไม่ต้องการคืนค่า null จากวิธีใด ๆ แต่ใช้ตัวเลือกประเภทการทำงานแทน วิธีการที่ไม่สามารถคืนค่าผลลัพธ์คืนค่าว่างเปล่าแทนค่า Null

นอกจากนี้วิธีการดังกล่าวที่ไม่สามารถส่งคืนผลลัพธ์ควรระบุด้วยชื่อของพวกเขา โดยปกติฉันจะใส่ Try หรือ TryGet หรือ TryFind ที่จุดเริ่มต้นของชื่อวิธีการเพื่อระบุว่ามันอาจส่งคืนผลลัพธ์ที่ว่างเปล่า (เช่น TryFindCustomer, TryLoadFile ฯลฯ )

ซึ่งช่วยให้ผู้โทรใช้เทคนิคที่แตกต่างกันเช่นการวางท่อในคอลเลกชัน (ดูที่ท่อเก็บของ Martin Fowler ) กับผลลัพธ์

นี่คืออีกตัวอย่างหนึ่งที่ใช้ตัวเลือกการส่งคืนแทนค่า null เพื่อลดความซับซ้อนของรหัส: วิธีการลดความซับซ้อนของวัฏจักร: ตัวเลือกประเภทการใช้งาน


1
ฉันเขียนคำตอบฉันเห็นว่ามันคล้ายกับของคุณเมื่อฉันเลื่อนขึ้นและฉันเห็นด้วยคุณสามารถใช้ตัวเลือกประเภทที่มีคอลเลกชันทั่วไปที่มีองค์ประกอบ 0 หรือ 1 ขอบคุณสำหรับลิงค์เพิ่มเติม
Gabriel P.

1

การบดเนื้อมากขึ้น: สมมุติว่า DAL ของฉันคืน NULL ให้กับ GetPersonByID ตามคำแนะนำของบางคน BLL (ค่อนข้างบาง) ฉันควรทำอย่างไรถ้าได้รับค่า NULL ส่ง NULL นั้นขึ้นและปล่อยให้ผู้บริโภคสิ้นกังวลเกี่ยวกับมัน (ในกรณีนี้หน้า ASP.Net) วิธีการเกี่ยวกับการโยน BLL ยกเว้น?

BLL อาจถูกใช้โดย ASP.Net และ Win App หรือไลบรารีคลาสอื่น - ฉันคิดว่ามันไม่ยุติธรรมที่จะคาดหวังให้ผู้บริโภคปลายทาง "รู้" จากภายในว่าวิธีการ GetPersonByID ส่งคืนค่า null (ยกเว้นว่ามีการใช้ประเภท null ฉันเดา )

สิ่งที่ฉันควรใช้ (สำหรับสิ่งที่ควรค่า) คือ DAL ของฉันจะส่งคืนค่า NULL หากไม่พบสิ่งใด สำหรับวัตถุประสงค์บางอย่างมันก็โอเค - อาจเป็น 0: รายการสิ่งต่าง ๆ ดังนั้นจึงไม่มีสิ่งใดที่ดี (เช่นรายการหนังสือที่ชื่นชอบ) ในกรณีนี้ BLL ของฉันจะส่งคืนรายการว่าง สำหรับสิ่งเดียวส่วนใหญ่ (เช่นผู้ใช้บัญชีใบแจ้งหนี้) ถ้าฉันไม่มีสิ่งนั้นเป็นปัญหาและมีข้อยกเว้นค่าใช้จ่ายสูง อย่างไรก็ตามเมื่อเห็นว่าการดึงข้อมูลผู้ใช้โดยตัวระบุที่ไม่ซ้ำกันซึ่งก่อนหน้านี้ได้รับจากแอปพลิเคชันควรส่งคืนผู้ใช้เสมอข้อยกเว้นคือข้อยกเว้น "เหมาะสม" เนื่องจากเป็นข้อยกเว้นที่ยอดเยี่ยม ผู้บริโภคขั้นสุดท้ายของ BLL (ASP.Net, f'rinstance) เท่านั้นที่คาดหวังว่าสิ่งต่าง ๆ จะเป็น hunky-dory ดังนั้น Handler Exception Handler ที่ไม่ถูกจัดการจะถูกใช้แทนการห่อทุก ๆ การเรียกไปที่ GetPersonByID ในบล็อก try-catch

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

ฉันเพลิดเพลินกับโพสต์นี้มีคำแนะนำที่ดีมากมายสำหรับสถานการณ์ "ขึ้นอยู่กับ" :-)


และแน่นอนวันนี้ฉันเจอสถานการณ์ที่ฉันจะคืน NULL จาก BLL ของฉัน ;-) ที่กล่าวว่าฉันยังคงสามารถโยนข้อยกเว้นและใช้ลอง / จับในชั้นเรียนการบริโภคของฉัน แต่ฉันยังมีปัญหา : ชั้นเรียนการบริโภคของฉันรู้ได้อย่างไรว่าใช้ลอง / จับเหมือนกันพวกเขารู้วิธีตรวจสอบค่า NULL ได้อย่างไร
Mike Kingscott

คุณสามารถจัดทำเอกสารว่าวิธีการส่งข้อยกเว้นผ่านทาง @throws doctag และคุณต้องการบันทึกความจริงที่ว่ามันสามารถคืนค่า null ใน @return doctag
Timoxley

1

ฉันคิดว่าฟังก์ชั่นไม่ควรคืนค่าว่างเพื่อสุขภาพของรหัสฐานของคุณ ฉันนึกถึงเหตุผลสองสามข้อ:

จะมีคำสั่งยามจำนวนมากปฏิบัติการอ้างอิงเป็นif (f() != null)ศูนย์

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

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

มีวิธีแก้ไขปัญหาบางอย่างtester-doer patternหรือการใช้งานoption typeจากการเขียนโปรแกรมฟังก์ชั่น


0

ฉันสับสนกับจำนวนคำตอบ (ทั่วทั้งเว็บ) ที่บอกว่าคุณต้องการสองวิธี: วิธี "IsItThere ()" และวิธี "GetItForMe ()" และสิ่งนี้นำไปสู่สภาพการแข่งขัน มีอะไรผิดปกติกับฟังก์ชั่นที่ส่งกลับค่า null กำหนดให้กับตัวแปรและตรวจสอบตัวแปรสำหรับ Null ทั้งหมดในการทดสอบ? รหัส C เดิมของฉันเต็มไปด้วย

if (NULL! = (ตัวแปร = ฟังก์ชั่น (อาร์กิวเมนต์ ... ))) {

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


0

nullผมเห็นด้วยกับการโพสต์มากที่สุดที่นี่ซึ่งมีแนวโน้มต่อ

เหตุผลของฉันคือการสร้างวัตถุว่างเปล่าที่มีคุณสมบัติไม่เป็นโมฆะอาจทำให้เกิดข้อบกพร่อง ตัวอย่างเช่นเอนทิตีที่มีint IDคุณสมบัติจะมีค่าเริ่มต้นID = 0ซึ่งเป็นค่าที่ถูกต้องทั้งหมด หากวัตถุนั้นได้รับการบันทึกลงในฐานข้อมูลในสถานการณ์บางอย่างมันจะเป็นสิ่งที่ไม่ดี

สำหรับทุกสิ่งที่มีตัววนซ้ำฉันมักจะใช้คอลเล็กชันที่ว่างเปล่า สิ่งที่ต้องการ

foreach (var eachValue in collection ?? new List<Type>(0))

รหัสคือกลิ่นในความคิดของฉัน คุณสมบัติการรวบรวมไม่ควรเป็นค่าว่าง

Stringกรณีที่ขอบ หลายคนพูดว่าString.IsNullOrEmptyไม่จำเป็นจริงๆ แต่คุณไม่สามารถแยกแยะความแตกต่างระหว่างสตริงว่างและ null ได้เสมอ นอกจากนี้ระบบฐานข้อมูลบางส่วน (Oracle) จะไม่แยกความแตกต่างระหว่างระบบเหล่านี้เลย ( ''ถูกจัดเก็บเป็นDBNULL) ดังนั้นคุณจะถูกบังคับให้จัดการอย่างเท่าเทียมกัน เหตุผลที่เป็นส่วนใหญ่ค่าสตริงอย่างใดอย่างหนึ่งมาจากการที่ผู้ใช้ป้อนหรือจากระบบภายนอกในขณะที่ค่า textboxes มิได้ที่สุดในรูปแบบการแลกเปลี่ยนมีการแสดงที่แตกต่างกันสำหรับการและ'' nullดังนั้นแม้ว่าผู้ใช้ต้องการลบค่าเขาไม่สามารถทำอะไรได้มากกว่าการล้างการควบคุมอินพุต นอกจากนี้ความแตกต่างของ nullable และไม่ใช่ nullablenvarcharเขตข้อมูลฐานข้อมูลที่เป็นเป็นมากกว่าที่น่าสงสัยถ้า DBMS ของคุณไม่ได้เป็นออราเคิล - ฟิลด์บังคับ''แปลกประหลาด UI ของคุณจะไม่อนุญาตสิ่งนี้ดังนั้นข้อ จำกัด ของคุณจึงไม่ได้จับคู่ ดังนั้นคำตอบที่นี่ในความคิดของฉันคือจัดการพวกเขาอย่างเท่าเทียมกันเสมอ

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


0

รูปแบบ TryGet แบบอะซิงโครนัส:

สำหรับวิธีการซิงโครผมเชื่อว่า@Johann Gerell ของ คำตอบคือรูปแบบที่จะใช้ในทุกกรณี

อย่างไรก็ตามรูปแบบ TryGet พร้อมoutพารามิเตอร์ไม่สามารถใช้ได้กับวิธีการ Async

ด้วย Tuple Literals ของ C # 7 คุณสามารถทำสิ่งนี้ได้:

async Task<(bool success, SomeObject o)> TryGetSomeObjectByIdAsync(Int32 id)
{
    if (InternalIdExists(id))
    {
        o = await InternalGetSomeObjectAsync(id);

        return (true, o);
    }
    else
    {
        return (false, default(SomeObject));
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.