ประเภท nullable จะดีกว่าตัวเลขมายากล?


22

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

ตัวแทนคนไหนดีกว่ากัน? ทั้งสองต้องมีการตรวจสอบเงื่อนไขสำหรับค่าความหมาย "none" แต่ฉันเชื่อว่าประเภท nullable บ่งบอกถึงความตั้งใจดีขึ้นเล็กน้อย


6
หากมีการใช้ตัวเลขให้ใส่ในค่าคงที่ไม่ใช่ในรหัสโดยตรง
Renato Dinhani

@ RenatoDinhaniConceiçãoที่ไม่สามารถเป็นกฎทั่วไป มิฉะนั้นคุณจะได้ซอฟต์โค้ดทุกอย่าง
Simon Bergot

คำตอบ:


24

พิจารณา:

  • ภาษา,

  • กรอบ,

  • บริบท.

1. ภาษา

การใช้∞สามารถแก้ปัญหาได้สูงสุด

  • ตัวอย่างเช่น JavaScript มีอินฟินิตี้ C # ไม่ได้¹

  • ตัวอย่างเช่น Ada มีช่วง C # ไม่ได้

ใน C # มีอยู่int.MaxValueแต่คุณไม่สามารถใช้งานได้ในกรณีของคุณ int.MaxValueเป็นจำนวนเต็มสูงสุด 2,147,483,647 หากในรหัสของคุณคุณมีค่าสูงสุดของบางสิ่งบางอย่างเช่นความดันสูงสุดที่ยอมรับได้ก่อนที่บางสิ่งจะระเบิดโดยใช้ 2,147,483,647 ไม่มีเหตุผล

2. กรอบงาน

.NET Framework ค่อนข้างไม่สอดคล้องกันในจุดนี้และการใช้ค่าเวทมนตร์สามารถถูกวิพากษ์วิจารณ์ได้

ตัวอย่างเช่นผลตอบแทนที่คุ้มค่ามายากล"Hello".IndexOf("Z") -1มันอาจจะทำให้ง่ายขึ้นในการจัดการผลลัพธ์:

int position = "Hello".IndexOf("Z");
if (position > 0)
{
    DoSomething(position);
}

แทนที่จะใช้โครงสร้างที่กำหนดเอง:

SearchOccurrence occurrence = "Hello".IndexOf("Z");
if (occurrence.IsFound)
{
    DoSomething(occurrence.StartOffset);
}

แต่ไม่ง่ายเลย ทำไม-1ไม่-123? ผู้เริ่มต้นอาจจะเข้าใจผิดคิดว่า0หมายถึง "ไม่พบ" (position >= 0)เกินไปหรือเพียงแค่พิมพ์ผิด

3. บริบท

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

class Timeout
{
    // A value indicating whether there is a timeout.
    public bool IsTimeoutEnabled { get; set; }

    // The duration of the timeout, in milliseconds.
    public int Duration { get; set; }
}
  • ฉันสามารถตั้งค่าDurationเป็น 0 ถ้าIsTimeoutEnabledจริงหรือไม่?
  • หากIsTimeoutEnabledเป็นเท็จจะเกิดอะไรขึ้นถ้าฉันตั้งDurationเป็น 100

สิ่งนี้สามารถนำไปสู่ข้อผิดพลาดหลายประการ ลองนึกภาพโค้ดต่อไปนี้:

this.currentOperation.Timeout = new Timeout
{
    // Set the timeout to 200 ms.; we don't want this operation to be longer than that.
    Duration = 200,
};

this.currentOperation.Run();

การดำเนินการทำงานเป็นเวลาสิบวินาที คุณเห็นว่ามีอะไรผิดปกติกับรหัสนี้โดยไม่อ่านเอกสารประกอบการTimeoutเรียนหรือไม่

ข้อสรุป

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

  • int.MaxValueมีความเกี่ยวข้องอย่างยิ่งกับภาษาของตัวเอง อย่าใช้int.MaxValueสำหรับการ จำกัด ความเร็วสูงสุดของVehicleคลาสหรือความเร็วสูงสุดที่ยอมรับได้สำหรับเครื่องบิน ฯลฯ

  • หลีกเลี่ยงค่าเวทย์มนตร์เช่น-1ในรหัสของคุณ พวกเขาทำให้เข้าใจผิดและนำไปสู่ข้อผิดพลาดในรหัส

  • สร้างคลาสของคุณเองซึ่งตรงไปตรงมามากขึ้นโดยระบุค่าต่ำสุด / สูงสุด ยกตัวอย่างเช่นสามารถมีVehicleSpeedVehicleSpeed.MaxValue

  • อย่าทำตามคำแนะนำก่อนหน้านี้และใช้ค่าเวทย์มนตร์หากเป็นแบบแผนทั่วไปมานานหลายทศวรรษในสาขาเฉพาะที่ใช้โดยคนส่วนใหญ่เขียนรหัสในสาขานี้

  • อย่าลืมผสมวิธี ตัวอย่างเช่น:

    class DnsQuery
    {
        public const int NoTimeout = 0;
    
        public int Timeout { get; set; }
    }
    
    this.query.Timeout = 0; // For people who are familiar with timeouts set to zero.
    // or
    this.query.Timeout = DnsQuery.NoTimeout; // For other people.
    

¹คุณสามารถสร้างประเภทของคุณเองซึ่งรวมถึงอินฟินิตี้ ที่นี่ฉันพูดถึงintประเภทพื้นเมืองเท่านั้น


1
"การใช้บางสิ่งบางอย่างที่ทุกคนใช้มานานหลายสิบปีเพื่อความสอดคล้องไม่ใช่ความคิดที่ไม่ดี" / "อย่าทำตามคำแนะนำก่อนหน้านี้และใช้ค่าเวทมนตร์หากเป็นการประชุมทั่วไปมานานหลายทศวรรษในสาขาเฉพาะที่ใช้โดย คนส่วนใหญ่เขียนโค้ดในฟิลด์นี้ " - มีบางอย่างที่ฉันคิดผิด?
deworde

1
@deworde ฉันเชื่อว่า MainMa หมายถึงแนวทางที่เขาให้ไว้ข้างต้น
Joshua Drake

1
ฉันไม่เห็นด้วยตัวอย่าง indexOf เนื่องจาก -1 อยู่นอกสตริงซึ่งZ เป็นส่วนใหญ่แน่นอน
Joshua Drake

5
"JavaScript เช่นมีค่าไม่สิ้นสุด C # ไม่มี" - เหรอ
BlueRaja - Danny Pflughoeft

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

12

Null ไม่ได้ดีไปกว่าจำนวนเวทย์มนตร์

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

if (timeout == 4298435) ... // bad.
if (timeout == null) ... // bad.
if (timeout == NEVER_TIME_OUT) ... // yay! puppies and unicorns!

2
ตกลงบางทีมันอาจจะขึ้นอยู่กับภาษา แต่ใน C # คุณอาจจะทำ: ถ้า (หมดเวลา .HasValue) แทนการเปรียบเทียบโดยตรงกับ null
Matt H

2
Null ไม่ได้เลวร้ายไปกว่าจำนวนเวทย์มนตร์ ด้วยหมายเลขมายากลคุณจะไม่มีทางรู้ว่าหมายเลขเวทคืออะไร ... มันอาจเป็น 0, -1 หรืออย่างอื่น null เป็นเพียง null
marco-fiset

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

2
การใช้ "null" เป็นค่าเวทมนต์ของคุณหากประเภทของคุณมี "null" จะไม่เป็นไร สิ่งที่สำคัญคือ NAME เพราะแน่นอนว่า shootin 'คนต่อไปที่เข้ามาจะไม่รู้ว่าคุณหมายถึงอะไร Null อาจหมายถึง "infinity," "ยังไม่ได้ระบุ" "บั๊กในรหัสที่สร้างโครงสร้างข้อมูล" หรือสิ่งอื่น ๆ มีเพียงชื่อเท่านั้นที่ให้ coder ถัดไปรู้ว่าคุณหมายถึงคุณค่าที่จะอยู่ที่นั่นและพฤติกรรมที่คุณต้องการให้เรียกใช้
mjfgates

1
@CodeInChaos: ฉันรู้ว่าคุณสามารถทำได้ทั้งคู่ แต่ฉันชอบ HasValue จริง ๆ แล้วฉันไม่ใช่แฟนตัวยงของโมฆะทั่วไป แต่ประเภท nullable โดยใช้ HasValue รู้สึกใกล้ชิดกับตัวเลือก / อาจจะพิมพ์ให้ฉันซึ่งฉันเป็นแฟนของ
Matt H

10

MAGIC_NUMBERควรหลีกเลี่ยงรหัสอย่างแน่นอนทุกที่ที่เป็นไปได้ nullเป็นการแสดงออกถึงเจตนาที่ชัดเจนกว่า


6

ใน C # คลาส CLR จำนวนมากมีEmptyสมาชิกแบบสแตติก:

  • System.String.Empty
  • System.EventArgs.Empty
  • System.Guid.Empty
  • System.Drawing.Rectangle.Empty
  • System.Windows.Size.Empty

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

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


3

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

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


3

ในกรณีนี้ฉันคิดว่าประเภท nullable เหมาะสมอย่างยิ่ง

Null หมายถึงการไม่มีค่า นี่เป็นแนวคิดที่แตกต่างอย่างชัดเจนกว่าตัวเลขที่มีค่าเป็น 0

หากคุณต้องการพูดว่า "ถ้าฉันไม่ให้คุณค่ากับคุณให้ใช้ค่าสูงสุด" ดังนั้นการส่งค่า null จะเป็นวิธีที่ถูกต้องในการแสดงสิ่งนั้น


1

Null:ค่าความผิดพลาดทั่วไป, ไม่ระบุ, เป็นโมฆะ, หรือไม่มีค่า

ศูนย์:ค่าจริง แต่ไม่จำเป็นต้องเป็นค่าตรรกะหรือสัญชาตญาณ (ในบริบทนี้) ยังเป็นค่าทั่วไปในการเริ่มต้น

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

สรุป:มีข้อยกเว้นและวิธีการแก้ไขแตกต่างกันไปตามภาษาและโดเมน; ในกรณีนี้ฉันจะเลือก Null ที่ (ฉันเชื่อ) บางคนเข้าใจผิดนี่คือเมื่อพวกเขาไม่แยกข้อมูลจากส่วนต่อประสานที่ดี พวกเขาคาดหวังว่าลูกค้าจะอ่านเอกสาร (หรือการนำไปใช้) เพื่อกำหนดว่าจะใช้ / จัดการค่าพิเศษเหล่านี้ได้อย่างไร - กรณีพิเศษรั่วไหลเข้าสู่โปรแกรมของลูกค้าและอาจไม่ชัดเจน โดยการเพิ่มเลเยอร์นามธรรมที่ดีการใช้งานจะชัดเจนขึ้น


0

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

ขึ้นอยู่กับสภาพแวดล้อม / ภาษาที่ใช้เป็นโมฆะได้

  • เป็นเพียง 0
  • อาจไม่ใช่ค่าทางกฎหมาย
  • อาจให้ผลลัพธ์ที่ไม่คาดคิดเนื่องจากตรรกะสามทาง

MagicNumber ทำตัวเหมือนเดิมเสมอ


0

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


-1

Null ไม่ได้เป็นทางเลือกเดียวกับหมายเลขมายากล

public static int NO_TIMEOUT = 0;  // javaish

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

Scala (ตัวอย่าง) มีทางเลือกที่ดีในคลาส Option คลาส Option มีหนึ่งในสองค่าซึ่งบางค่าจะตัดค่าที่คุณต้องการและไม่มี - ซึ่งไม่มีค่า

สิ่งนี้ทำให้นักพัฒนาซอฟต์แวร์เห็นได้อย่างชัดเจนว่าอาจไม่มีคุณค่าและคุณมีรหัสที่ดีกว่า ก็ควรทำให้ชัดเจนอยู่ดี

และไม่ใช่ว่าเลขอาถรรพ์ทั้งหมดจะเป็นปัญหา ขึ้นอยู่กับบริบท 0, 1, 1024 และอื่น ๆ ทั้งหมดได้ชัดเจน 347? ใช่ว่าคุณควรหลีกเลี่ยง :-)


4
-1: แสดงให้เห็นถึง "null is evil"
deworde

4
การกำหนดชื่อแทนสำหรับตัวเลขนั้นไม่ได้เปลี่ยนความจริงที่ว่ามันยังคงเป็นเลขอาคม
17 ของ 26

บางทีคุณอาจมีคำจำกัดความของจำนวนเวทย์มนตร์แตกต่างจากฉัน โปรดดูen.wikipedia.org/wiki/…
Jon Strayer

1
ฉันเห็นด้วยกับจอน Strayer ที่นี่ Null เป็นตัวอย่างของ ADT ในภาษาที่ไม่สนับสนุน ADT OP น่าจะหนีไปได้ที่นี่ แต่โดยทั่วไปฉันคิดว่าภาษาใดที่มีค่า null ที่ล้มเหลวก็คือโปรแกรมเมอร์เล็กน้อย
Jeremy Wall
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.