Enum ควรเริ่มต้นด้วย 0 หรือ 1 หรือไม่?


136

ลองนึกภาพฉันได้กำหนด Enum ต่อไปนี้:

public enum Status : byte
{
    Inactive = 1,
    Active = 2,
}

การปฏิบัติที่ดีที่สุดในการใช้ enum คืออะไร มันควรเริ่มต้นด้วย1เช่นตัวอย่างข้างต้นหรือเริ่มต้นด้วย0(ไม่มีค่าที่ชัดเจน) ดังนี้:

public enum Status : byte
{
    Inactive,
    Active
}

13
คุณจำเป็นต้องระบุหมายเลขให้ชัดเจนหรือไม่?
Yuck

164
Enums ถูกสร้างขึ้นเพื่อให้สิ่งต่าง ๆ เช่นนี้ไม่สำคัญ
BoltClock

9
@Daniel - ไม่สิ! ดีกว่าที่จะใช้ enum เมื่อคุณคิดว่าบูลีนจะทำได้ดีกว่าการใช้บูลีนเมื่อคุณกำลังคิดว่าเป็น enum
AAT

22
@Daniel เนื่องจากค่า FileNotFound แน่นอน
Joubarc

5
xkcd.com/163ใช้กับ enum ได้ดียิ่งขึ้นกว่าดัชนีอาร์เรย์
leftaroundabout

คำตอบ:


161

แนวทางการออกแบบกรอบ :

✔️ให้ค่าศูนย์ใน enums ง่าย

ลองโทรหาค่าบางอย่างเช่น "ไม่มี" หากค่าดังกล่าวไม่เหมาะสมสำหรับ enum นี้ค่าเริ่มต้นที่พบบ่อยที่สุดสำหรับ enum ควรได้รับการกำหนดค่าพื้นฐานเป็นศูนย์

แนวทางการออกแบบกรอบงาน / การออกแบบธง Enums :

❌หลีกเลี่ยงการใช้ค่าสถานะ enum เป็นศูนย์ยกเว้นค่าที่แสดงถึง "ค่าสถานะทั้งหมดจะถูกล้าง" และตั้งชื่ออย่างเหมาะสมตามที่กำหนดโดยแนวทางต่อไป

✔️ตั้งชื่อศูนย์ค่าของการตั้งค่าสถานะ enums ไม่มี สำหรับ enum แฟล็กค่าต้องหมายถึง "เคลียร์แฟล็กทั้งหมด" เสมอ


28
ล้มเหลวในช่วงต้น: หาก 'ไม่มี' ไม่เหมาะสม แต่ไม่มีค่าเริ่มต้นเชิงตรรกะฉันยังคงใส่ค่าเป็นศูนย์ (และเรียกว่า 'ไม่มี' หรือ 'ไม่ถูกต้อง') ซึ่งไม่ได้มีไว้เพื่อใช้ ถ้าเป็นสมาชิกระดับของการแจงนับว่าไม่ได้เริ่มต้นอย่างถูกต้องค่า uninitialized สามารถจะเห็นและในswitchงบมันจะข้ามไปยังส่วนที่ฉันโยนdefault InvalidEnumArgumentExceptionมิฉะนั้นโปรแกรมอาจทำงานโดยไม่ตั้งใจต่อไปโดยมีค่าศูนย์ของการแจงนับซึ่งอาจถูกต้องและไม่ได้สังเกต
Allon Guralnek

1
@Allon ดูเหมือนจะดีกว่าที่จะให้ค่า enum ที่คุณรู้ว่าถูกต้องแล้วตรวจสอบค่าที่ไม่ถูกต้องใน setter และ / หรือ Constructor ด้วยวิธีนี้คุณจะรู้ได้ทันทีว่าบางรหัสทำงานไม่ถูกต้องแทนที่จะปล่อยให้วัตถุที่มีข้อมูลที่ไม่ถูกต้องอยู่ในเวลาที่ไม่ทราบและหาข้อมูลในภายหลัง นอกจากว่า 'ไม่มี' หมายถึงสถานะที่ถูกต้องคุณไม่ควรใช้มัน
wprl

@SoloBold: ดูเหมือนกรณีที่คุณไม่ลืมที่จะเริ่มต้นสมาชิกระดับ enum ในนวกรรมิก จำนวนการตรวจสอบจะไม่ช่วยถ้าคุณลืมที่จะเริ่มต้นหรือลืมที่จะตรวจสอบ นอกจากนี้ยังมีคลาส DTO แบบง่าย ๆ ที่ไม่มีคอนสตรัคเตอร์ใด ๆ แต่ต้องอาศัยobject initializersแทน การตามล่าบั๊กเช่นนี้อาจทำให้เจ็บปวดอย่างมาก อย่างไรก็ตามการเพิ่มค่าการแจงนับที่ไม่ได้ใช้ทำให้น่าเกลียด API ฉันจะหลีกเลี่ยงสำหรับ API ที่มุ่งเน้นเพื่อการบริโภคสาธารณะ
Allon Guralnek

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

@SoloBold: ลองนึกภาพคลาส DTO กับ 15 คุณสมบัติที่นำไปใช้โดยอัตโนมัติ ลำตัวยาว 15 บรรทัด ทีนี้ลองนึกภาพชั้นเรียนเดียวกันว่ามีคุณสมบัติปกติ นั่นคือขั้นต่ำ 180 บรรทัดก่อนที่จะเพิ่มตรรกะการตรวจสอบใด ๆ คลาสนี้ถูกใช้ภายในเพื่อการถ่ายโอนข้อมูลเท่านั้น คุณจะเลือกแบบไหนเป็นคลาส 15 ไลน์หรือ 180+ คลาส? ความกะทัดรัดมีคุณค่า แต่ถึงกระนั้นสไตล์ของเราทั้งคู่ก็ถูกต้องพวกมันต่างกัน (ฉันเดาว่านี่คือสิ่งที่ AOP เข้ามาและชนะทั้งสองด้านของการโต้แย้ง)
Allon Guralnek

66

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


9
+1, ตกลง แต่เฉพาะในกรณีที่รหัสของคุณอาศัยจำนวนเต็มด้วยเหตุผลภายนอกบางอย่าง (เช่นการทำให้เป็นอนุกรม) ทุก ๆ ที่ที่คุณควรติดกับการปล่อยให้กรอบทำหน้าที่ของมัน หากคุณพึ่งพาค่าจำนวนเต็มภายในคุณอาจทำสิ่งผิดปกติ (ดู: ให้เฟรมเวิร์กทำงานได้)
Matthew Scharley

3
ฉันชอบที่จะยืนยันข้อความของ enum ทำให้ฐานข้อมูล imo ใช้งานได้มากขึ้น
เดฟ

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

4
@Dave: หากคุณไม่อธิบายเอกสารที่ enum ตำราเป็นสิ่งศักดิ์สิทธิ์คุณตั้งค่าตัวเองสำหรับความล้มเหลวหากโปรแกรมเมอร์ในอนาคตตัดสินใจที่จะปรับชื่อให้ชัดเจนขึ้นหรือสอดคล้องกับการตั้งชื่อบางอย่าง
supercat

1
@ pstjds: มันเป็นการค้าโวหารฉันเดา - พื้นที่ดิสก์มีราคาถูกแม้ว่าเวลาที่ใช้ตลอดเวลาแปลงระหว่างค่า enum และ intwhen เมื่อค้นหาฐานข้อมูลค่อนข้างแพง (ทวีคูณดังนั้นถ้าคุณมีเครื่องมือการตั้งค่าการรายงานกับฐานข้อมูลหรือสิ่งที่คล้ายกัน) . หากคุณกังวลเกี่ยวกับพื้นที่ abotu ด้วยเวอร์ชันเซิร์ฟเวอร์ SQL ที่ใหม่กว่าคุณสามารถบีบอัดฐานข้อมูลได้ซึ่งหมายความว่า "SomeEnumTextualValue" ที่เกิดขึ้น 1,000 รายการจะใช้พื้นที่อีกต่อไป แน่นอนว่าสิ่งนี้จะไม่สามารถใช้ได้กับทุกโครงการ - เป็นการแลกเปลี่ยน ฉันคิดว่ากังวลเกี่ยวกับแบนด์วิดท์กลิ่นเช่นการเพิ่มประสิทธิภาพก่อนวัยอันควรอาจจะ!
เดฟ

15

Enum เป็นประเภทค่าและค่าเริ่มต้น (ตัวอย่างเช่นเขตข้อมูล Enum ในคลาส) จะเป็น 0 หากไม่ได้กำหนดค่าเริ่มต้นอย่างชัดเจน

ดังนั้นโดยทั่วไปคุณต้องการให้ 0 เป็นค่าคงที่ที่กำหนด (เช่นไม่ทราบ)

ในตัวอย่างของคุณถ้าคุณต้องการInactiveให้เป็นค่าเริ่มต้นมันควรจะมีค่าเป็นศูนย์ Unknownมิฉะนั้นคุณอาจต้องการที่จะพิจารณาการเพิ่มอย่างต่อเนื่อง

บางคนแนะนำว่าคุณไม่ได้ระบุค่าสำหรับค่าคงที่ของคุณอย่างชัดเจน คำแนะนำที่ดีอาจเป็นไปได้ในกรณีส่วนใหญ่ แต่มีบางกรณีที่คุณต้องการทำเช่นนี้:

  • ตั้งค่าสถานะ enums

  • Enums ที่มีค่าจะถูกใช้ใน interop กับระบบภายนอก (เช่น COM)


ฉันพบว่าค่าสถานะ enums สามารถอ่านได้มากขึ้นเมื่อคุณไม่ได้ตั้งค่าอย่างชัดเจน - นอกจากนี้ยังมีข้อผิดพลาดน้อยที่จะทำให้คอมไพเลอร์ทำการคำนวณเลขฐานสองสำหรับคุณ (เช่น[Flags] enum MyFlags { None = 0, A, B, Both = A | B, /* etc. */ }เป็นวิธีที่อ่านง่ายกว่า[Flags] enum MyFlags { None = 0, A = 1, B = 2, Both = 3, /* etc */ })
BrainSlugs83

1
@ BrainSlugs83 - ฉันไม่เห็นว่าจะเป็นประโยชน์ในกรณีทั่วไป - เช่น[Flags] enum MyFlags { None=0, A, B, C } จะส่งผลใน[Flags] enum MyFlags { None=0, A=1, B=2, C=3 }ขณะที่สำหรับ enum ธงคุณมักจะต้องการ C = 4
Joe

14

ยกเว้นว่าคุณมีเหตุผลที่เฉพาะเจาะจงในการเปลี่ยนแปลงปล่อยให้ enums ด้วยค่าเริ่มต้นซึ่งเริ่มต้นที่ศูนย์

public enum Status : byte
{
    Inactive,
    Active
}

6

ฉันจะบอกว่าวิธีปฏิบัติที่ดีที่สุดคือการไม่ให้หมายเลขพวกเขาและปล่อยให้มันเป็นนัย - ซึ่งจะเริ่มต้นจาก 0 เนื่องจากมันเป็นการตั้งค่าภาษาที่แน่นอนซึ่งดีต่อการติดตามเสมอ :)


6

ฉันจะเริ่มต้นประเภทบูลีน Enum ด้วย 0

เว้นแต่ "Inative" หมายถึงสิ่งอื่นที่ไม่ใช่ "Inactive" :)

ยังคงรักษามาตรฐานสำหรับสิ่งเหล่านั้น


6

ฉันจะบอกว่ามันขึ้นอยู่กับว่าคุณใช้มันอย่างไร สำหรับการตั้งค่าสถานะ enum เป็นวิธีที่ดีที่จะมีNoneค่า0 เช่นนั้น:

[Flags]
enum MyEnum
{
    None = 0,
    Option1 = 1,
    Option2 = 2,
    Option3 = 4,
    All = Option1 | Option2 | Option3,
}

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

ในกรณีอื่น ๆ ฉันจะทิ้งไว้เหมือนเดิมโดยไม่สนใจว่าพวกเขาจะเริ่มต้นด้วย 0 หรือ 1


5

ถ้าคุณมีเหตุผลที่ดีที่จะใช้ค่าดิบคุณเท่านั้นที่เคยควรจะใช้ค่าปริยายและการอ้างอิงถึงพวกเขาด้วยและStatus.ActiveStatus.Inactive

การจับคือคุณอาจต้องการเก็บข้อมูลในไฟล์ flat หรือ DB หรือใช้ flat file หรือ DB ที่คนอื่นสร้างขึ้น หากคุณสร้างด้วยตัวเองให้กำหนดหมายเลขให้พอดีกับที่ใช้ Enum

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

หากคุณวางแผนที่จะใช้ Enum เป็นชุดของธงมีการประชุมที่เรียบง่ายที่คุ้มค่าต่อไปนี้:

enum Example
{
  None      = 0,            //  0
  Alpha     = 1 << 0,       //  1
  Beta      = 1 << 1,       //  2
  Gamma     = 1 << 2,       //  4
  Delta     = 1 << 3,       //  8
  Epsilon   = 1 << 4,       // 16
  All       = ~0,           // -1
  AlphaBeta = Alpha | Beta, //  3
}

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



3

หากไม่ระบุหมายเลขเริ่มต้นที่ 0

สิ่งสำคัญคือต้องมีความชัดเจนเนื่องจาก enums มักจะต่อเนื่องและจัดเก็บเป็น int ไม่ใช่สตริง

สำหรับ enum ใด ๆ ที่เก็บไว้ในฐานข้อมูลเราจะกำหนดหมายเลขตัวเลือกอย่างชัดเจนเพื่อป้องกันการเลื่อนและการกำหนดใหม่ในระหว่างการบำรุงรักษา

ตามที่ Microsoft อนุสัญญาที่แนะนำคือใช้ตัวเลือกศูนย์แรกเพื่อแสดงค่าเริ่มต้นหรือค่าเริ่มต้นทั่วไป

ด้านล่างนี้เป็นทางลัดเพื่อเริ่มหมายเลขที่ 1 แทน 0

public enum Status : byte
{
    Inactive = 1,
    Active
}

หากคุณต้องการตั้งค่าแฟล็กเพื่อใช้ตัวดำเนินการบิตกับค่า enum อย่าเริ่มต้นการกำหนดหมายเลขที่ค่าศูนย์


2

หากคุณเริ่มต้นที่ 1 คุณสามารถนับสิ่งของได้อย่างง่ายดาย

{
    BOX_THING1     = 1,
    BOX_THING2     = 2,
    BOX_NUM_THING  = BOX_THING2
};

หากคุณเริ่มต้นที่ 0 ให้ใช้อันแรกเป็นค่าสำหรับสิ่งที่ไม่ได้กำหนดค่าเริ่มต้น

{
    BOX_NO_THING   = 0,
    BOX_THING1     = 1,
    BOX_THING2     = 2,
    BOX_NUM_THING  = BOX_THING2
};

5
ขอโทษนะโจนาธาน ฉันคิดว่าคำแนะนำนี้เป็น "โรงเรียนเก่า" ในใจของฉัน (ประเภทของการประชุมมาจากระดับต่ำ -c ปี) นี่เป็นวิธีแก้ปัญหาที่รวดเร็วในการ "ฝัง" ข้อมูลเพิ่มเติมบางส่วนเกี่ยวกับ enum แต่นี่ไม่ใช่วิธีปฏิบัติที่ดีในระบบที่มีขนาดใหญ่กว่า คุณไม่ควรใช้ enum หากคุณต้องการข้อมูลเกี่ยวกับจำนวนของค่าที่มีเป็นต้นและ BOX_NO_THING1 เป็นอย่างไร คุณจะให้เขา BOX_NO_THING +1 ควรใช้ Enums เป็นค่าที่ใช้สำหรับ: ค่า (int) เฉพาะที่แสดงด้วยชื่อ "พูด"
Beachwalker

ฮึ่ม คุณสมมติว่าเป็นโรงเรียนเก่าเพราะฉันใช้ตัวพิมพ์ใหญ่ทั้งหมดฉันเดาว่าแทนที่จะเป็น MicrosoftBumpyCaseWithLongNames แม้ว่าฉันจะเห็นด้วยว่าควรใช้ตัววนซ้ำมากกว่าการวนซ้ำจนกว่าจะถึงนิยาม XyzNumDefsInMyEnum ของ enum'ed
Jonathan Cline IEEE

นี่เป็นวิธีปฏิบัติที่เลวร้ายใน C # ในหลากหลายวิธี ตอนนี้เมื่อคุณไปรับจำนวน enums ของคุณวิธีที่ถูกต้องหรือถ้าคุณพยายามที่จะระบุพวกเขาในวิธีที่ถูกต้องคุณจะได้รับวัตถุพิเศษซ้ำกัน นอกจากนี้ยังทำให้การโทร. ToString () อาจคลุมเครือ (การทำให้เป็นอันดับต่อเนื่องที่ทันสมัย) เหนือสิ่งอื่นใด
BrainSlugs83

0

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

ประการที่สองคุณควรมีรายการค่าเป็นศูนย์เสมอ (ในค่าที่ไม่ใช่แฟล็ก enums) องค์ประกอบนั้นจะถูกใช้เป็นค่าเริ่มต้น


0

อย่าเริ่มต้นที่ 0 เว้นแต่จะมีเหตุผลเช่นใช้เป็นดัชนีในอาร์เรย์หรือรายการหรือถ้ามีเหตุผลเชิงปฏิบัติอื่น ๆ (เช่นใช้พวกมันในการทำงานระดับบิต)

คุณenumควรเริ่มตรงที่มันต้องการ มันไม่จำเป็นต้องเรียงตามลำดับเช่นกัน ค่าหากมีการตั้งค่าไว้อย่างชัดเจนจำเป็นต้องสะท้อนความหมายเชิงความหมายหรือการพิจารณาในทางปฏิบัติ ตัวอย่างเช่นenumควรระบุหมายเลข "ขวดบนผนัง" จาก 1 ถึง 99 ในขณะที่ค่าenumกำลัง 4 ควรเริ่มที่ 4 และดำเนินการต่อด้วย 16, 64, 256 และอื่น ๆ

นอกจากนี้การเพิ่มองค์ประกอบที่มีค่าเป็นศูนย์ในenumควรจะทำก็ต่อเมื่อมันแสดงถึงสถานะที่ถูกต้อง บางครั้ง "none," "unknown," "missing," ฯลฯ เป็นค่าที่ถูกต้อง แต่หลายครั้งก็ไม่ใช่


-1

ฉันชอบที่จะเริ่ม enums ที่ 0 เนื่องจากเป็นค่าเริ่มต้น แต่ฉันต้องการรวมค่าที่ไม่รู้จักด้วยค่า -1 นี่จะกลายเป็นค่าเริ่มต้นและสามารถช่วยแก้จุดบกพร่องได้ในบางครั้ง


4
ความคิดที่น่ากลัว ในฐานะที่เป็นประเภทค่า enums จะเริ่มต้นได้เป็นศูนย์เสมอ หากคุณกำลังจะมีค่าบางอย่างที่ไม่รู้จักหรือไม่ได้กำหนดค่าเริ่มต้นมันจะต้องเป็น 0 คุณไม่สามารถเปลี่ยนค่าเริ่มต้นเป็น -1 ได้โดยที่การเติมศูนย์ไม่มีการเข้ารหัสแบบฮาร์ดตลอด CLR
Ben Voigt

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