What does an enum with the [Flags] attribute mean and how does it work?

6

I was seeing how the FileInfo class works and I came across an enum:

[Serializable]
[ComVisible(true)]
[Flags]
public enum FileAttributes
{
    ReadOnly = 1,
    Hidden = 2,
    System = 4,
    Directory = 16,
    Archive = 32,
    Device = 64,
    Normal = 128,
    Temporary = 256,
    SparseFile = 512,
    ReparsePoint = 1024,
    Compressed = 2048,
    Offline = 4096,
    NotContentIndexed = 8192,
    Encrypted = 16384,
    [ComVisible(false)]
    IntegrityStream = 32768,
    [ComVisible(false)]
    NoScrubData = 131072,
}

What does this [Flags] attribute do?

    
asked by anonymous 03.01.2014 / 19:13

2 answers

12

You can use bitwise operations to combine different values into just one.

To add a value, use the OR :

var rgb = Cores.Vermelho | Cores.Verde | Cores.Azul;

To check if a value is present, use the AND operator (see method HasFlag below):

if ((rgb & Cores.Azul) == Cores.Azul)

To remove a value use the NOT operator:

rgb &= ~Cores.Azul;

Enum.HasFlag *

If you are using .NET 4 or higher you can use the HasFlag method to check whether the value of the last enum is in the combination:

if (rgb.HasFlag(Cores.Azul))

Beware of no multiple of two

As you may have noticed, the combination is done with the bits of each combined value.

That is, if any of the values are not a multiple of two you may be combining several flags at once.

Example:

public enum Cores
{
    Vermelho = 1, // Cor primária.
    Verde = 2, // Cor primária.
    Amarelo = 3, // Cor segundária. Mistura de vermelho com verde (1 & 2).
    Azul = 4, // Cor primária.
    Magenta = 5, // Cor segundária. Mistura de vermelho e azul (1 & 4).
    Ciano = 6 // Cor segundária. Mistura de verde e azul (2 & 4).
}

var cores = Cores.Ciano | Cores.Magenta | Cores.Amarelo;

The variable cores will have not only the flags Ciano , Magenta and Amarelo but also will have Vermelho , Verde and Azul :

cores.HasFlag(Cores.Vermelho); // True
cores.HasFlag(Cores.Verde); // True
cores.HasFlag(Cores.Azul); // True

But ... and the Flags attribute anyway?

Please note that we are not talking about the Flags attribute so far. Everything that has been said so far works without the enum being marked with the attribute.

The difference between an enum marked with Flags and an unmarked one is:

  • It changes the behavior of certain methods such as Format and ToString .
  • It is especially useful in languages other than C #, such as VB, that do not support bitwise operations at language level .
Although the documentation makes explicit that the HasFlag method only works with enums marked with FlagsAttribute , according to my tests it worked normally with an enum Unmarked. Anyway follow the excerpt:

  The HasFlag method is designed to be used with enumeration types that are marked with the FlagsAttribute attribute and can be used to determine whether multiple bit fields are set. For enumeration types that are not marked with the FlagsAttribute attribute, call either the Equals method or the CompareTo method.

    
03.01.2014 / 20:50
4

An enum with this attribute allows you to put multiple values into a single variable. For example:

var atributosDoArquivo = FileAttributes.ReadOnly | FileAttributes.Hidden;

The enum marked with the [Flags] attribute works by doing bitwise operations, that is, in a simpler example:

[Flags]
public enum MeuEnum
{
    Um   = 1 << 0,       // 1
    Dois = 1 << 1,       // 2
    Tres = 1 << 2,       // 4
}

* It's okay to use the << operator, because in this case the values are resolved at compile time.

Note that for correct operation you need to put the bit in the correct position because:

// valores do enum
Um   = 00000001
Dois = 00000010
Tres = 00000100

So when we assign two values of this enum to a variable, the result will be:

var resultado = MeuEnum.Um | MeuEnum.Tres; // 00000101

The bit of Um and Tres are present in the result, so this variable contains both values.

If your variable has more than one value and you want to know if it contains only one value, you should not compare the variable directly as you might have thought, because if it contains more than a value your condition will fail.

To find out if resultado has Um and any other value, compare this way:

if ((resultado & MeuEnum.Um) == MeuEnum.Um)

Well

// resultado & MeuEnum.Um == MeuEnum.Um
(00000101 & 00000001) = 00000001

If you want to remove only one value while keeping all others, you can use:

resultado &= ~MeuEnum.Um;

Well

// resultado & inverso de Um = Tres
00000101     & 11111110      = 00000100

More here about C # operators in MSDN .

    
03.01.2014 / 19:13