Enum nên bắt đầu bằng 0 hay 1?


136

Hãy tưởng tượng tôi đã định nghĩa Enum sau đây:

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

Thực hành tốt nhất để sử dụng enum là gì? Nên bắt đầu bằng 1ví dụ trên hoặc bắt đầu bằng 0(không có giá trị rõ ràng) như thế này:

public enum Status : byte
{
    Inactive,
    Active
}

13
Bạn có thực sự cần phải đánh số chúng rõ ràng không?
Yuck

164
Enums được tạo ra chỉ để những thứ như thế này sẽ không quan trọng.
BoltClock

9
@Daniel - không có! Tốt hơn là sử dụng một enum khi bạn nghĩ rằng một boolean sẽ làm hơn là sử dụng một boolean khi bạn đang nghĩ về một enum.
AAT

22
@Daniel vì giá trị FileNotFound, tất nhiên
Joubarc

5
xkcd.com/163 áp dụng cho enum thậm chí còn tốt hơn so với các chỉ số mảng.
rẽ trái

Câu trả lời:


161

Nguyên tắc thiết kế khung :

✔️ DO cung cấp giá trị bằng 0 trên các enum đơn giản.

Hãy xem xét việc gọi giá trị này là "Không." Nếu một giá trị như vậy không phù hợp với enum cụ thể này, giá trị mặc định phổ biến nhất cho enum sẽ được gán giá trị cơ bản bằng không.

Nguyên tắc thiết kế khung / Thiết kế cờ Enums :

❌ TRÁNH sử dụng các giá trị cờ enum bằng 0 trừ khi giá trị đại diện cho "tất cả các cờ bị xóa" và được đặt tên thích hợp, theo quy định của hướng dẫn tiếp theo.

✔️ NÊN đặt tên cho giá trị 0 của enum cờ Không. Đối với một enum cờ, giá trị phải luôn có nghĩa là "tất cả các cờ bị xóa."


28
Thất bại sớm: Nếu 'Không' không phù hợp nhưng không có giá trị mặc định hợp lý, tôi vẫn đặt giá trị bằng 0 (và gọi đó là 'Không' hoặc 'Không hợp lệ') không được sử dụng, chỉ để nếu một thành viên của lớp liệt kê đó không được khởi tạo đúng, giá trị chưa được khởi tạo có thể dễ dàng được phát hiện, và trong các switchcâu lệnh, nó sẽ nhảy đến defaultphần, nơi tôi ném a InvalidEnumArgumentException. Mặt khác, một chương trình có thể vô tình tiếp tục chạy với giá trị 0 của một phép liệt kê, có thể hợp lệ và không được chú ý.
Allon Guralnek

1
@ ALLon Có vẻ tốt hơn là chỉ cung cấp các giá trị enum mà bạn biết là hợp lệ và sau đó kiểm tra các giá trị không hợp lệ trong setter và / hoặc constructor. Bằng cách đó, bạn biết ngay nếu một số mã không hoạt động chính xác thay vì cho phép một đối tượng có dữ liệu không hợp lệ tồn tại trong một thời gian không xác định và tìm hiểu về nó sau. Trừ khi 'Không' đại diện cho trạng thái hợp lệ, bạn không nên sử dụng nó.
wprl

@SoloBold: Nghe có vẻ giống như trường hợp bạn không quên khởi tạo một thành viên lớp enum trong hàm tạo. Không có số lượng xác nhận sẽ giúp bạn nếu bạn quên khởi tạo hoặc quên xác nhận. Ngoài ra, có những lớp DTO đơn giản không có bất kỳ hàm tạo nào, mà chỉ dựa vào các trình khởi tạo đối tượng . Săn lùng một con bọ như vậy có thể vô cùng đau đớn. Tuy nhiên, việc thêm một giá trị liệt kê không sử dụng sẽ tạo ra một API xấu xí. Tôi sẽ tránh nó cho các API hướng đến tiêu dùng công cộng.
Allon Guralnek

@ ALLon Đó là một điểm tốt, nhưng tôi sẽ lập luận rằng bạn nên xác thực các enum trong hàm setter và ném từ getter nếu setter không bao giờ được gọi. Bằng cách đó, bạn có một điểm thất bại và bạn có thể lập kế hoạch trên trường luôn có giá trị hợp lệ, giúp đơn giản hóa thiết kế và mã. Dù sao hai xu của tôi.
wprl

@SoloBold: Hãy tưởng tượng một lớp DTO với 15 thuộc tính được triển khai tự động. Cơ thể của nó dài 15 dòng. Bây giờ hãy tưởng tượng rằng cùng một lớp với các thuộc tính thông thường. Đó là tối thiểu 180 dòng trước khi thêm bất kỳ logic xác minh nào. Lớp này được sử dụng riêng trong nội bộ cho mục đích truyền dữ liệu. Cái nào bạn muốn duy trì, một lớp 15 dòng hoặc một lớp 180+? Sự gọn gàng có giá trị của nó. Tuy nhiên, cả hai phong cách của chúng tôi đều chính xác, đơn giản là chúng khác nhau. (Tôi đoán đây là nơi AOP càn quét và chiến thắng cả hai mặt của cuộc tranh luận).
Allon Guralnek

66

Chà, tôi đoán là tôi không đồng ý với hầu hết các câu trả lời nói không đánh số chúng một cách rõ ràng. Tôi luôn luôn đánh số chúng một cách rõ ràng, nhưng đó là bởi vì trong hầu hết các trường hợp, cuối cùng tôi vẫn tồn tại chúng trong một luồng dữ liệu nơi chúng được lưu trữ dưới dạng một giá trị nguyên. Nếu bạn không thêm các giá trị một cách rõ ràng và sau đó thêm một giá trị mới, bạn có thể phá vỡ tuần tự hóa và sau đó không thể tải chính xác các đối tượng tồn tại cũ. Nếu bạn định thực hiện bất kỳ loại lưu trữ liên tục nào của các giá trị này thì tôi khuyên bạn nên thiết lập rõ ràng các giá trị.


9
+1, đã đồng ý, nhưng chỉ trong trường hợp mã của bạn dựa vào số nguyên vì một số lý do bên ngoài (ví dụ: tuần tự hóa). Ở mọi nơi khác, bạn nên gắn bó với việc chỉ để khung làm việc. Nếu bạn dựa vào các giá trị số nguyên trong nội bộ, thì có lẽ bạn đang làm sai điều gì đó (xem: hãy để khung làm việc đó).
Matthew Scharley

3
Tôi thích tiếp tục các văn bản của enum. Làm cho cơ sở dữ liệu imo có thể sử dụng nhiều hơn.
Dave

2
Thông thường, bạn không cần thiết lập các giá trị một cách rõ ràng ... ngay cả khi chúng được tuần tự hóa. chỉ cần LUÔN thêm các giá trị mới vào cuối. điều này sẽ giải quyết các vấn đề nối tiếp. nếu không, bạn có thể cần một phiên bản của kho lưu trữ dữ liệu của mình (ví dụ: tiêu đề tệp chứa phiên bản để thay đổi hành vi khi đọc / ghi giá trị) (hoặc xem mẫu memento)
Beachwalker

4
@Dave: Trừ khi bạn khám phá tài liệu rằng các văn bản enum là thiêng liêng, bạn sẽ tự đặt ra thất bại nếu một lập trình viên tương lai quyết định điều chỉnh tên để rõ ràng hơn hoặc tuân theo quy ước đặt tên nào đó.
supercat

1
@pstrjds: Tôi đoán đó là một sự đánh đổi phong cách - không gian đĩa rất rẻ, thời gian chuyển đổi liên tục giữa các giá trị enum và khi tìm kiếm db tương đối tốn kém (vì vậy, nếu bạn có thiết lập công cụ báo cáo dựa trên cơ sở dữ liệu hoặc một cái gì đó tương tự) . Nếu bạn lo lắng về không gian abotu, với các phiên bản máy chủ SQL mới hơn, bạn có thể nén cơ sở dữ liệu, điều đó có nghĩa là 1000 lần xuất hiện của "someEnumTextualValue" sẽ sử dụng gần như không gian nữa. Tất nhiên điều này sẽ không hoạt động cho tất cả các dự án - đó là một sự đánh đổi. Tôi nghĩ rằng sự lo lắng về băng thông có mùi như tối ưu hóa sớm, có thể!
Dave

15

Enum là một loại giá trị và giá trị mặc định của nó (ví dụ cho trường Enum trong một lớp) sẽ là 0 nếu không được khởi tạo một cách rõ ràng.

Do đó, bạn thường muốn có 0 là một hằng số xác định (ví dụ: Unknown).

Trong ví dụ của bạn, nếu bạn muốn Inactivelà mặc định, thì nó phải có giá trị 0. Nếu không, bạn có thể muốn xem xét thêm một hằng số Unknown.

Một số người đã khuyến nghị rằng bạn không chỉ định rõ ràng các giá trị cho các hằng số của mình. Có thể là lời khuyên tốt trong hầu hết các trường hợp, nhưng có một số trường hợp bạn sẽ muốn làm như vậy:

  • Cờ enums

  • Enums có giá trị được sử dụng trong interop với các hệ thống bên ngoài (ví dụ COM).


Tôi đã thấy rằng các cờ enums dễ đọc hơn rất nhiều khi bạn không đặt các giá trị một cách rõ ràng. - Cũng là cách ít lỗi hơn để cho trình biên dịch thực hiện phép toán nhị phân cho bạn. (tức [Flags] enum MyFlags { None = 0, A, B, Both = A | B, /* etc. */ }là cách dễ đọc hơn, hơn [Flags] enum MyFlags { None = 0, A = 1, B = 2, Both = 3, /* etc */ }.)
BrainSlugs83

1
@ BrainSlugs83 - Tôi không thấy điều đó sẽ hữu ích như thế nào trong trường hợp chung - ví dụ như [Flags] enum MyFlags { None=0, A, B, C } sẽ dẫn đến kết quả [Flags] enum MyFlags { None=0, A=1, B=2, C=3 }, trong khi đối với một lá cờ enum bạn thường muốn C = 4.
Joe

14

Trừ khi bạn có một lý do cụ thể để thay đổi nó, hãy để lại các enum với các giá trị mặc định của chúng, bắt đầu từ 0.

public enum Status : byte
{
    Inactive,
    Active
}

6

Tôi muốn nói rằng cách tốt nhất là không đánh số chúng và để nó ẩn - điều này sẽ bắt đầu từ 0. Vì nó ẩn theo sở thích ngôn ngữ luôn luôn tốt để theo dõi :)


6

Tôi sẽ bắt đầu một kiểu boolean enum với 0.

Trừ khi "Inative" có nghĩa là một cái gì đó khác với "Không hoạt động" :)

Điều này giữ lại tiêu chuẩn cho những người.


6

Tôi muốn nói, nó phụ thuộc vào cách bạn sử dụng chúng. Để gắn cờ enum, cách tốt nhất là có 0 cho Nonegiá trị, như thế:

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

Khi enum của bạn có khả năng được ánh xạ tới bảng tra cứu cơ sở dữ liệu, tôi sẽ bắt đầu với 1. Nó không quan trọng lắm đối với mã được viết chuyên nghiệp, nhưng điều này giúp cải thiện khả năng đọc.

Trong các trường hợp khác, tôi sẽ để nguyên như vậy, không quan tâm họ bắt đầu bằng 0 hay 1.


5

Trừ khi bạn có lý do chính đáng để sử dụng các giá trị thô, bạn chỉ nên sử dụng các giá trị ngầm định và tham chiếu chúng với Status.ActiveStatus.Inactive.

Điều hấp dẫn là bạn có thể muốn lưu trữ dữ liệu trong một tệp phẳng hoặc DB hoặc sử dụng một tệp phẳng hoặc DB mà người khác đã tạo. Nếu bạn tự làm, hãy làm cho nó để đánh số phù hợp với những gì Enum được sử dụng cho.

Nếu dữ liệu không phải là của bạn, tất nhiên bạn sẽ muốn sử dụng bất cứ thứ gì mà nhà phát triển ban đầu đã sử dụng làm sơ đồ đánh số.

Nếu bạn đang dự định sử dụng Enum như một bộ cờ, có một quy ước đơn giản đáng để tuân theo:

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
}

Các giá trị phải là lũy thừa của hai và có thể được biểu thị bằng các phép toán bit-shift. None, rõ ràng là nên 0, nhưng Allít rõ ràng hơn -1. ~0là phủ định nhị phân 0và kết quả là một số có mọi bit được đặt thành 1, đại diện cho một giá trị-1 . Đối với cờ ghép (thường được sử dụng để thuận tiện), các giá trị khác có thể được hợp nhất bằng cách sử dụng bitwise hoặc toán tử |.


3

Đừng gán bất kỳ số nào. Chỉ cần sử dụng nó như nó được sử dụng.


3

Nếu không chỉ định đánh số bắt đầu từ 0.

Điều quan trọng là phải rõ ràng vì enums thường được tuần tự hóa và được lưu trữ dưới dạng int, không phải là một chuỗi.

Đối với bất kỳ enum nào được lưu trữ trong cơ sở dữ liệu, chúng tôi luôn đánh số rõ ràng các tùy chọn để ngăn việc dịch chuyển và phân công lại trong quá trình bảo trì.

Theo Microsoft, quy ước được đề xuất là sử dụng tùy chọn số 0 đầu tiên để thể hiện giá trị mặc định chưa được khởi tạo hoặc giá trị mặc định phổ biến nhất.

Dưới đây là một phím tắt để bắt đầu đánh số 1 thay vì 0.

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

Nếu bạn muốn đặt giá trị cờ để sử dụng toán tử bit trên giá trị enum, đừng bắt đầu đánh số ở giá trị 0.


2

Nếu bạn bắt đầu từ 1, thì bạn có thể dễ dàng đếm được những thứ của mình.

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

Nếu bạn bắt đầu từ 0, thì hãy sử dụng giá trị đầu tiên làm giá trị cho những thứ chưa được khởi tạo.

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

5
Xin lỗi, Jonathan. Tôi nghĩ rằng, gợi ý này là một "trường học cũ" nhỏ trong tâm trí của tôi (loại quy ước xuất phát từ những năm cấp thấp). Đây là một giải pháp nhanh chóng để "nhúng" một số thông tin bổ sung về enum nhưng đây không phải là một cách thực hành tốt trong các hệ thống lớn hơn. Bạn không nên sử dụng enum nếu bạn cần thông tin về số lượng giá trị khả dụng, v.v ... Còn BOX_NO_THING1 thì sao? Bạn có thể cho anh ấy BOX_NO_THING + 1 không? Enums nên được sử dụng như những gì chúng được sử dụng cho: Giá trị cụ thể (int) được biểu thị bằng tên "nói".
Beachwalker

Hừm. Bạn cho rằng đó là trường học cũ bởi vì tôi đã sử dụng tất cả các mũ, tôi đoán, thay vì MicrosoftBumpyCaseWithLongNames. Mặc dù tôi đồng ý sử dụng các trình vòng lặp tốt hơn là lặp cho đến khi đạt được định nghĩa XyzNumDefsInMyEnum.
Jonathan Cline IEEE

Đây là thực tế khủng khiếp trong C # theo nhiều cách khác nhau. Bây giờ khi bạn đi để đếm số enum của bạn đúng cách, hoặc nếu bạn cố gắng liệt kê chúng theo cách chính xác, bạn sẽ nhận được một đối tượng trùng lặp bổ sung. Ngoài ra, nó làm cho lệnh gọi .ToString () có khả năng mơ hồ (làm hỏng việc tuần tự hóa hiện đại), trong số những thứ khác.
BrainSlugs83

0

Trước hết, trừ khi bạn chỉ định các giá trị cụ thể vì một lý do (giá trị số có ý nghĩa ở một nơi khác, tức là Cơ sở dữ liệu hoặc dịch vụ bên ngoài), sau đó không chỉ định giá trị số và để chúng rõ ràng.

Thứ hai, bạn phải luôn luôn có một mục giá trị bằng không (trong enum không có cờ). Phần tử đó sẽ được sử dụng làm giá trị mặc định.


0

Đừng bắt đầu chúng ở 0 trừ khi có lý do, chẳng hạn như sử dụng chúng làm chỉ mục cho một mảng hoặc danh sách hoặc nếu có một số lý do thực tế khác (như sử dụng chúng trong các hoạt động theo bit).

Bạn enumnên bắt đầu chính xác nơi nó cần. Nó cũng không cần phải tuần tự. Các giá trị, nếu chúng được đặt rõ ràng, cần phản ánh một số ý nghĩa ngữ nghĩa hoặc xem xét thực tế. Ví dụ: một enum"chai trên tường" nên được đánh số từ 1 đến 99, trong khi một enumsức mạnh của 4 có lẽ nên bắt đầu từ 4 và tiếp tục với 16, 64, 256, v.v.

Hơn nữa, việc thêm một phần tử có giá trị bằng 0 vào enumchỉ nên được thực hiện nếu nó đại diện cho trạng thái hợp lệ. Đôi khi "không", "không xác định", "thiếu", v.v. là các giá trị hợp lệ, nhưng nhiều lần thì không.


-1

Tôi muốn bắt đầu số điện thoại của mình ở mức 0, vì đó là mặc định, nhưng tôi cũng muốn bao gồm một giá trị Không xác định, với giá trị -1. Điều này sau đó trở thành mặc định và đôi khi có thể giúp gỡ lỗi.


4
Ý tưởng khủng khiếp. Là một loại giá trị, enums luôn được khởi tạo về không. Nếu bạn sẽ có một số giá trị đại diện cho không xác định hoặc chưa được khởi tạo, thì nó cần bằng 0. Bạn không thể thay đổi mặc định thành -1, điền 0 được mã hóa cứng trong suốt CLR.
Ben Voigt

Ah, tôi đã không nhận ra điều đó. Tôi thường đặt giá trị của một giá trị enum / thuộc tính khi tôi decalre / khởi tạo. Cảm ơn con trỏ.
Tomas McGuinness
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.