2017-02-14 22 views
0

У меня есть следующие enum:Проблемы с Enum как Флаги

[Flags] 
public enum Status { Nominal, Modified, DirOneOnly, DirTwoOnly, DirOneNewest, DirTwoNewest } 

Я пытаюсь увидеть, был ли Modified бит был установлен верно и попытался следующие методы:

if(_stateFlags.HasFlag(Status.Modified)) 
{ 
    //DoStuff 
} //Found out why this doesn't work after reading docs. 

и

if((_stateFlags & Status.Modified) == Status.Modified) 
{ 
    //DoStuff 
} 

Последний метод, который мои дальнейшие исследования привели я полагаю, что это сработает. Однако, когда я делаю _stateFlags = Status.DirTwoOnly, приведенное выше заявление все еще, кажется, оценивает true, что действительно озадачивает меня.

Я делаю что-то принципиально неправильно?

+0

Вы присвоили ему атрибут [Флаги], но перечисления не имеют однозначных значений. (int) Статус = 3 может быть DirTwoOnly или может быть номинальным | Modified. Вы должны их пронумеровать, чтобы они не перекрывали биты. Итак, 1, 2, 4, 8, 16 и т. Д. –

+0

Спасибо @HansPassant Я предположил, что они дефолтны по силам двух. Виноват. –

+0

Вы можете подумать о том, стоит ли определять вашу собственную структуру, которая по-прежнему занимает всего 4 байта, но имеет на ней собственные методы, такие как «IsModified» и т. Д. Необходимость выполнения битовой арифметики для определения ее значения делает код, который должен читать как семантика - «изменен статус?«- читайте больше как механизмы -« бит три из этого битового массива включен? » –

ответ

6

You need to define the enum constants as powers of two.

[Flags] 
public enum Status 
{ 
    Nominal = 1, 
    Modified = 2, 
    DirOneOnly = 4, 
    DirTwoOnly = 8, 
    DirOneNewest = 16, 
    DirTwoNewest = 32 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Status s = new Status(); 
     s |= Status.Modified; 
     if (s.HasFlag(Status.Modified)) 
     { 
      Console.WriteLine("Modified!"); 
     } 
    } 
} 
+0

О, я предположил, что это значение по умолчанию, я соглашусь, когда ограничение времени –

+0

Атрибуты - это всего лишь метаданные и ca не изменяйте поведение переписей по умолчанию, подобное этому, но было бы неплохо иметь интуитивно понятный механик для таких флагов. – TVOHM

+0

Было бы неплохо, нет причин (что я знаю), которые помешали бы компилятору изменить то, как он делает что-то, основанное на атрибуте. –

1

Код:

[Flags] 
public enum Status { Nominal, Modified, DirOneOnly, DirTwoOnly, DirOneNewest, DirTwoNewest } 

равно:

[Flags] 
public enum Status 
{ 
    Nominal  = 0, // in bits: ... 0000 0000 
    Modified  = 1, //    0000 0001 
    DirOneOnly = 2, //    0000 0010 
    DirTwoOnly = 3, //    0000 0011 **common bit with Modified state ** 
    DirOneNewest = 4, 
    DirTwoNewest = 5, 

}

Итак, как сказал, что другие люди, вы должны использовать силу 2 для enum val ЕЭС.

+0

Спасибо @apocolypse, я предположил, что с атрибутом '[Flags]' он переходил от приращения на 1 до двух степеней. –

7

У вас есть несколько ответов на ваш вопрос, объясняющие, что не так. Я бы предложил совсем другой подход.

Флаги перечислены в течение 1990-х годов; если вы считаете, что они похожи на COM-взаимодействие, и если вы считаете, что COM-перечисления выглядят так, как будто они совместимы с битовым кодом с 1970-х годов, вы правы.

Я бы не сказал эту логику как перечисление в новом коде. Я бы потратил время на создание настраиваемой структуры, которая четко выражает мою фактическую семантику.

struct Status 
{ 
    public static readonly Status None = default(Status); 
    private Status(int bits) { this.bits = bits; } 
    private int bits; 
    private const int NominalBitMask = 0b0001; 
    private const int ModifiedBitMask = 0b0010; 
    ... etc ... 
    public bool IsNominal => (this.bits & NominalBitMask) != 0; 
    public Status WithNominal(bool f) => 
    new Status(f ? (this.bits | NominalBitMask) : (this.bits & ~NominalBitMask)); 
    ... etc ... 

И теперь вы можете использовать его как:

Status status = Status.None.WithNominal(true).WithModified(myFile.IsModified); 
... 
if (status.IsModified) ... 

ли это больше работы впереди? Конечно, уже около двадцати минут работы. Но вы никогда больше не совершаете какую-либо ошибку. У вас есть структура, которую вы можете тестировать независимо от логики, которая ее использует. У вас есть код, который выглядит так, как он. Вам никогда не придется беспокоиться о том, что кто-то бросает целое число в ваш тип перечисления и имеет полное чушь. Вы можете поместить пользовательскую логику в свой тип; предположим, например, существуют трехзначные флаги - истинные, ложные или нулевые, скажем - это трудно сделать в перечислениях флагов, но вы можете легко добавить логику в пользовательский тип. И так далее.

+0

Вы порекомендовали бы это сделать для перечислений без флага? Или это будет излишним? (предполагая, что нет необходимости добавлять какие-либо методы или свойства к типу). –