2010-01-25 5 views
6

Я понимаю, как Enums работают на C#, и я получаю то, что атрибут Flags приносит в таблицу.Несколько способов определить C# Enums с атрибутом [Flags]?

Я видел этот вопрос, here. Что рекомендует первый аромат, но не дает никаких оснований/оправданий для него.

Есть ли разница в том, как эти два определены, один лучше другого? В чем преимущества использования первого синакса вместо второго? Я всегда использовал второй аромат при определении типа Enums Enums ... я все время делал это неправильно?

[Serializable] 
[Flags] 
public enum SiteRoles 
{ 
    User = 1 << 0, 
    Admin = 1 << 1, 
    Helpdesk = 1 << 2 
} 

Это не то же самое, как

[Serializable] 
[Flags] 
public enum SiteRoles 
{ 
    User = 1, 
    Admin = 2, 
    Helpdesk = 4 
} 
+0

Код IL, созданный для этих двух фрагментов кода, является тем же. –

+3

Нарисуйте ошибку в этом коде: BackupOperator = 1073714824. Вы можете избежать ошибки в первую очередь, сказав BackupOperator = 1 << 30 –

+0

Спасибо за информацию обо всем, я буду использовать первый aproach, так как это выглядит лучше для все, кроме самых простых случаев. – Nate

ответ

6

Главное преимущество первого заключается в том, что вам не нужно вычислять правильные значения для каждого флага, поскольку компилятор сделает это за вас. Кроме того, они одинаковы.

+0

Значит, это в основном компилятор трюк? – Nate

+2

Да, 1 << n - постоянная, поэтому компилятор должен вычислить ее, которая может быть менее подвержена ошибкам, чем 1,2,4,8 ... Вы также можете использовать hex, например. 0x1, 0x10, 0x100 ... – Lee

+1

Хотя вас могут заинтересовать шестнадцатеричные значения, такие как 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80. –

0

AFAIK сво дискуссии читаемости. Некоторые скажут, что первый из них более читабельен, потому что у вас есть фактический индекс флага с правой стороны от «< <».

+0

Действительно ли это эффект трюка компилятора? Так как 1 << 2 = 4? – Nate

+1

Это не трюк компилятора. В чем разница между Helpdesk = 1 << 2, Helpdesk = 4 или Helpdesk = 3 + 1. Это просто выражение, которое оценивается. – empi

+0

Думаю, я вижу это как преимущество компилятора и, следовательно, трюк компилятора. Ваша точка зрения хорошо взята. – Nate

6

Рассмотрим более сложные образцы:

[Flags] 
public enum SiteRoles 
{ 
    User = 1 << 12, 
    Admin = 1 << 13, 
    Helpdesk = 1 << 15, 
    AdvancedUser = User | Helpdesk, //or (1<<12)|(1<<13) 
} 

[Flags] 
public enum SiteRoles 
{ 
    User = 4096, //not so obvious! 
    Admin = 8192, 
    Helpdesk = 16384, 
    AdvancedUser = 12288, //! 
} 

[Flags] 
public enum SiteRoles 
{ 
    User = 0x1000, //we can use hexademical digits 
    Admin = 0x2000, 
    Helpdesk = 0x4000, 
    AdvancedUser = 0x3000, //it much simpler calculate binary operator OR with hexademicals 
} 

Эти образцы показывают, что в этом случае первая версия является более удобным для чтения. Десятичные литералы - не лучший способ представления констант флага. А для получения дополнительной информации о побитовых операциях (которые также могут использоваться для представления констант флагов) см. http://en.wikipedia.org/wiki/Bitwise_operation

0

Существует еще один способ сделать это, что довольно изящно, и поэтому я решил поделиться тем, что я недавно написал. Он имеет преимущество в том, что требует очень мало математики, и поэтому я думаю, что он менее подвержен ошибкам. Это очень читаемо, ИМХО.

[Flags][Serializable] 
public enum ScopeType : int 
{ 
    Unknown = 0, 
    Global = 1, 
    Namespace = Global << 1, 
    Class = Namespace << 1, 
    Struct = Class << 1, 
    Interface = Struct << 1, 
    Enum = Interface << 1, 
    Function = Enum << 1, 
    Property = Function << 1, 
    PropertyGetter = Property << 1, 
    PropertySetter = PropertyGetter << 1, 
    Using = PropertySetter << 1, 
    If = Using << 1, 
    ElseIf = If << 1, 
    Else = ElseIf << 1, 
    Switch = Else << 1, 
    Case = Switch << 1, 
    For = Case << 1, 
    While = For << 1, 
    DoWhile = While << 1, 
    Lambda = DoWhile << 1, 
    Try = Lambda << 1, 
    Catch = Try << 1, 
    Finally = Catch << 1, 
    Initializer = Finally << 1, 
    Checked = Initializer << 1, 
    Unchecked = Checked << 1, 
    Unsafe = Unchecked << 1, 
    Lock = Unsafe << 1, 
    Fixed = Lock << 1, 

    // I can also group flags together using bitwise-OR. 
    PropertyAccessor = PropertyGetter | PropertySetter, 
    TypeDefinition = Class | Struct | Interface | Enum, 
    TryCatchFinally = Try | Catch | Finally, 
    Conditional = If | ElseIf | Else, 
    Branch = Conditional | Case | TryCatchFinally, 
    Loop = For | While | DoWhile 
} 

Примечание: Поскольку перечисление наследует от System.Int32, я могу определить только 32 флагов. Если вам нужно больше, вам нужно будет использовать большее целое число (System.Int64), создать более одного перечисления и связать их вместе или просто создать класс с букетом логических значений.