Tại sao quyền enum thường có các giá trị 0, 1, 2, 4?


159

Tại sao mọi người luôn sử dụng giá trị enum thích 0, 1, 2, 4, 8và không 0, 1, 2, 3, 4?

Đây có phải là một cái gì đó để làm với các hoạt động bit, vv?

Tôi thực sự sẽ đánh giá cao một đoạn mẫu nhỏ về cách sử dụng chính xác :)

[Flags]
public enum Permissions
{
    None   = 0,
    Read   = 1,
    Write  = 2,
    Delete = 4
}


25
Tôi không đồng ý với phiếu bầu.
zzzzBov

Cách UNIX để đặt quyền cũng dựa trên logic tương tự.
Rudy

3
@Pascal: Bạn có thể thấy hữu ích khi đọc về Bitwise OR (và Bitwise AND ), đó là những gì |(và &) đại diện. Các câu trả lời khác nhau cho rằng bạn đã quen thuộc với nó.
Brian

2
@IAd CHƯƠNG Tôi có thể thấy lý do tại sao bạn nghĩ rằng, vì câu trả lời cho cả hai đều giống nhau, nhưng tôi nghĩ các câu hỏi là khác nhau. Câu hỏi khác chỉ yêu cầu một ví dụ hoặc giải thích về thuộc tính Flags trong C #. Câu hỏi này dường như là về khái niệm cờ bit, và các nguyên tắc cơ bản đằng sau chúng.
Jeremy S

Câu trả lời:


268

Bởi vì họ là sức mạnh của hai và tôi có thể làm điều này:

var permissions = Permissions.Read | Permissions.Write;

Và có lẽ sau này ...

if( (permissions & Permissions.Write) == Permissions.Write )
{
    // we have write access
}

Đó là một trường bit, trong đó mỗi bit set tương ứng với một số quyền (hoặc bất cứ giá trị liệt kê nào tương ứng một cách hợp lý). Nếu những điều này được xác định là 1, 2, 3, ...bạn sẽ không thể sử dụng các toán tử bitwise theo cách này và nhận được kết quả có ý nghĩa. Để tìm hiểu sâu hơn ...

Permissions.Read   == 1 == 00000001
Permissions.Write  == 2 == 00000010
Permissions.Delete == 4 == 00000100

Chú ý một mẫu ở đây? Bây giờ nếu chúng ta lấy ví dụ ban đầu của tôi, tức là,

var permissions = Permissions.Read | Permissions.Write;

Sau đó...

permissions == 00000011

Xem? Cả bit ReadWritebit đều được đặt và tôi có thể kiểm tra độc lập (Cũng lưu ý rằng Deletebit không được đặt và do đó giá trị này không truyền đạt quyền xóa).

Nó cho phép một người lưu trữ nhiều cờ trong một trường bit.


2
@Malcolm: Nó có; myEnum.IsSet. Tôi cho rằng đây là một sự trừu tượng hoàn toàn vô dụng và chỉ dùng để cắt giảm việc đánh máy, nhưng meh
Ed S.

1
Câu trả lời hay, nhưng bạn nên đề cập đến lý do tại sao thuộc tính Cờ được áp dụng và khi nào bạn cũng không muốn áp dụng Cờ cho một số enum.
Andy

3
@Andy: Trên thực tế, Flagsthuộc tính không làm gì nhiều ngoài việc cung cấp cho bạn 'in ấn đẹp'. Bạn có thể sử dụng một giá trị được liệt kê làm cờ bất kể sự hiện diện của thuộc tính.
Ed S.

3
@detly: Bởi vì nếu các câu lệnh trong C # yêu cầu một biểu thức boolean. 0là không false; falsefalse. Tuy nhiên bạn có thể viết if((permissions & Permissions.Write) > 0).
Ed S.

2
Thay vì 'khó khăn' (permissions & Permissions.Write) == Permissions.Write, giờ đây bạn có thể sử dụngenum.HasFlag()
Louis Kottmann

147

Nếu vẫn chưa rõ ràng từ các câu trả lời khác, hãy nghĩ về nó như thế này:

[Flags] 
public enum Permissions 
{   
   None = 0,   
   Read = 1,     
   Write = 2,   
   Delete = 4 
} 

chỉ là một cách ngắn hơn để viết:

public enum Permissions 
{   
    DeleteNoWriteNoReadNo = 0,   // None
    DeleteNoWriteNoReadYes = 1,  // Read
    DeleteNoWriteYesReadNo = 2,  // Write
    DeleteNoWriteYesReadYes = 3, // Read + Write
    DeleteYesWriteNoReadNo = 4,   // Delete
    DeleteYesWriteNoReadYes = 5,  // Read + Delete
    DeleteYesWriteYesReadNo = 6,  // Write + Delete
    DeleteYesWriteYesReadYes = 7, // Read + Write + Delete
} 

Có tám khả năng nhưng bạn có thể đại diện cho chúng dưới dạng kết hợp chỉ có bốn thành viên. Nếu có mười sáu khả năng thì bạn có thể đại diện cho chúng dưới dạng kết hợp chỉ năm thành viên. Nếu có bốn tỷ khả năng thì bạn có thể đại diện cho họ dưới dạng kết hợp chỉ 33 thành viên! Rõ ràng là tốt hơn nhiều khi chỉ có 33 thành viên, mỗi (trừ 0) sức mạnh của hai, hơn là cố gắng đặt tên cho bốn tỷ vật phẩm trong một enum.


32
+1 cho hình ảnh tinh thần của một enumvới bốn tỷ thành viên. Và điều đáng buồn là, có lẽ ai đó ngoài kia đã thử nó.
Daniel Pryden

23
@DanielPryden Là một độc giả hàng ngày của Daily WTF, tôi tin điều đó.
fluffy

1
2 ^ 33 = ~ 8,6 tỷ. Đối với 4 tỷ giá trị khác nhau, bạn chỉ cần 32 bit.
một CVn

5
@ MichaelKjorling một trong số 33 là dành cho 0 mặc định
ratchet freak

@ MichaelKjorling: Công bằng mà nói, chỉ có 32 thành viên là sức mạnh của 2, vì 0 không phải là sức mạnh của hai. Vì vậy, "33 thành viên, mỗi người một sức mạnh của hai" là không chính xác (trừ khi bạn được tính 2 ** -infinitylà sức mạnh của hai).
Brian

36

Bởi vì các giá trị này đại diện cho các vị trí bit duy nhất trong nhị phân:

1 == binary 00000001
2 == binary 00000010
4 == binary 00000100

v.v.

1 | 2 == binary 00000011

BIÊN TẬP:

3 == binary 00000011

3 trong nhị phân được biểu thị bằng giá trị 1 ở cả hai vị trí và vị trí twos. Nó thực sự giống như giá trị 1 | 2. Vì vậy, khi bạn đang cố gắng sử dụng các vị trí nhị phân làm cờ để biểu thị một số trạng thái, 3 thường không có ý nghĩa (trừ khi có một giá trị logic thực sự là sự kết hợp của cả hai)

Để làm rõ hơn, bạn có thể muốn mở rộng ví dụ của mình như sau:

[Flags]
public Enum Permissions
{
  None = 0,   // Binary 0000000
  Read = 1,   // Binary 0000001
  Write = 2,  // Binary 0000010
  Delete = 4, // Binary 0000100
  All = 7,    // Binary 0000111
}

Do đó trong tôi có Permissions.All, tôi cũng ngầm có Permissions.Read, Permissions.WritePermissions.Delete


và vấn đề với 2 | 3 là gì?
Pascal

1
@Pascal: Vì 311nhị phân, tức là nó không ánh xạ tới một bit set duy nhất, do đó bạn mất khả năng ánh xạ 1 bit ở vị trí tùy ý thành một giá trị có ý nghĩa.
Ed S.

8
@Pascal đặt cách khác , 2|3 == 1|3 == 1|2 == 3. Vì vậy, nếu bạn có một giá trị với nhị phân 00000011, và cờ của bạn bao gồm các giá trị 1, 23, sau đó bạn sẽ không biết nếu giá trị đại diện 1 and 3, 2 and 3, 1 and 2hoặc only 3. Điều đó làm cho nó ít hữu ích hơn nhiều.
yshavit

10
[Flags]
public Enum Permissions
{
    None   =    0; //0000000
    Read   =    1; //0000001
    Write  = 1<<1; //0000010
    Delete = 1<<2; //0000100
    Blah1  = 1<<3; //0001000
    Blah2  = 1<<4; //0010000
}

Tôi nghĩ rằng viết như thế này dễ hiểu và dễ đọc hơn và bạn không cần phải tính toán nó.


5

Chúng được sử dụng để biểu diễn các cờ bit cho phép kết hợp các giá trị enum. Tôi nghĩ sẽ rõ ràng hơn nếu bạn viết các giá trị bằng ký hiệu hex

[Flags]
public Enum Permissions
{
  None =  0x00,
  Read =  0x01,
  Write = 0x02,
  Delete= 0x04,
  Blah1 = 0x08,
  Blah2 = 0x10
}

4
@Pascal: Có lẽ nó dễ đọc hơn đối với bạn tại thời điểm này, nhưng khi bạn có được kinh nghiệm xem byte trong hex sẽ trở thành bản chất thứ hai. Hai chữ số trong ánh xạ hex thành một byte ánh xạ thành 8 bit (dù sao ... một byte thường là 8 bit dù sao ... không phải lúc nào cũng đúng, nhưng với ví dụ này, bạn có thể khái quát hóa được).
Ed S.

5
@Pascal nhanh lên, bạn nhận được gì khi nhân 41943042? Thế còn 0x400000? 0x800000Câu trả lời chính xác sẽ dễ dàng hơn nhiều 8388608và cũng ít bị lỗi hơn khi nhập giá trị hex.
phoog

6
Nhìn nhanh hơn rất nhiều, nếu nhìn thoáng qua, nếu cờ của bạn được đặt đúng (nghĩa là sức mạnh của 2), nếu bạn sử dụng hex. Là 0x10000một sức mạnh của hai? Có, nó bắt đầu bằng 1, 2, 4 hoặc 8 và có tất cả 0 sau đó. Bạn không cần phải dịch 0x10 thành 16 về mặt tinh thần (mặc dù làm như vậy có thể sẽ trở thành bản chất thứ hai), chỉ cần nghĩ về nó như là "sức mạnh của 2".
Brian

1
Tôi hoàn toàn với Jared về việc ghi chú trong hex dễ dàng hơn nhiều. bạn chỉ cần sử dụng 1 2 4 8 và thay đổi
bevacqua

1
Cá nhân, tôi thích chỉ sử dụng ví dụ P_READ = 1 << 0, P_WRITE = 1 <, 1, P_RW = P_READ | P_WRITE. Tôi không chắc loại sắp xếp liên tục đó có hoạt động trong C # hay không nhưng nó hoạt động tốt trong C / C ++ (cũng như Java, tôi nghĩ vậy).
fluffy

1

Đây thực sự là một nhận xét nhiều hơn, nhưng vì nó không hỗ trợ định dạng, tôi chỉ muốn bao gồm một phương pháp tôi đã sử dụng để thiết lập bảng liệt kê cờ:

[Flags]
public enum FlagTest
{
    None = 0,
    Read = 1,
    Write = Read * 2,
    Delete = Write * 2,
    ReadWrite = Read|Write
}

Tôi thấy cách tiếp cận này đặc biệt hữu ích trong quá trình phát triển trong trường hợp bạn muốn duy trì cờ của mình theo thứ tự bảng chữ cái. Nếu bạn xác định bạn cần thêm một giá trị cờ mới, bạn chỉ có thể chèn nó theo thứ tự bảng chữ cái và giá trị duy nhất bạn phải thay đổi là giá trị hiện có trước đó.

Tuy nhiên, lưu ý rằng một khi giải pháp được công bố cho bất kỳ hệ thống sản xuất nào (đặc biệt là nếu enum bị lộ mà không có khớp nối chặt chẽ, chẳng hạn như qua dịch vụ web), thì rất nên chống lại việc thay đổi bất kỳ giá trị hiện có nào trong enum.


1

Của rất nhiều câu trả lời tốt với trang này ... Tôi sẽ chỉ nói .. nếu bạn không thích, hoặc không thể dễ dàng nắm bắt những gì các <<cú pháp đang cố gắng thể hiện .. Cá nhân tôi thích một sự thay thế (và tôi dám nói, đơn giản enum phong cách khai) Giáo dục

typedef NS_OPTIONS(NSUInteger, Align) {
    AlignLeft         = 00000001,
    AlignRight        = 00000010,
    AlignTop          = 00000100,
    AlignBottom       = 00001000,
    AlignTopLeft      = 00000101,
    AlignTopRight     = 00000110,
    AlignBottomLeft   = 00001001,
    AlignBottomRight  = 00001010
};

NSLog(@"%ld == %ld", AlignLeft | AlignBottom, AlignBottomLeft);

LOG 513 == 513

Vì vậy, dễ dàng hơn nhiều (đối với bản thân tôi, ít nhất) để hiểu. Xếp hàng những người mà Mô tả kết quả mà bạn mong muốn, nhận được kết quả mà bạn MUỐN .. Không cần "tính toán".

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.