C# - Usando o atributo Flags em enumerações


 Hoje veremos como usar o atributo Flags em enumerações na linguagem C#.

O atributo Flags indica que uma enumeração pode ser tratada como um campo de bits, ou seja, um conjunto de sinalizadores.

A ideia do atributo Flags é pegar uma variável de enumeração e permitir que ela contenha vários valores. Este recurso deve ser usado sempre que o enum representar uma coleção de sinalizadores, em vez de representar um único valor.

Essas coleções de enumeração são geralmente manipuladas usando operadores bit a bit. Você cria um enum de sinalizadores de bit aplicando o atributo System.FlagsAttribute e definindo os valores apropriadamente, de forma que as operações bit a bit AND, OR, NOT e XOR possam ser realizadas neles.

Assim o atributo Flags em um enum permite que você atribua vários valores ao seu enum de uma vez. Você pode fazer isso com manipulações bit a bit, o que significa que você pode armazenar em um enum mais de um valor.

Vejamos um exemplo prático usando um projeto Console criado no VS 2019 com o nome EnumFlags:

Primeiro vamos definir a enumeração usando o atributo Flags:

    [Flags]
    enum TipoRenderezicao
    {
        None = 0x0,
        DataUri = 0x1,
        GZip = 0x2,
        ContentPage = 0x4,
        ViewPage = 0x8,
        HomePage = 0x10 // proximos valores podem ser 0x20, 0x40
    }

Os valores 0x0, 0x1, 0x2, 0x4 e assim por diante indicam potências de dois. Em bits de computador, potências de dois contêm um conjunto de bits, movendo-se do primeiro para o último. Você pode usar os valores decimais, 0, 1, 2, 4, 8 ... em vez disso.

A seguir vamos incluir o código no método Main onde vamos definir o tipo de renderização e fazer a checagem:

static void Main(string[] args)
{
   // define o primeiro tipo
   TipoRenderezicao tipo1 = TipoRenderezicao.ContentPage;
    // Define o segundo tipo se a condição conferir
    if (true)
    {
         tipo1 |= TipoRenderezicao.GZip;
    }

    // Verifica a enumeração flags.
    Verifica(tipo1);
    Console.ReadLine();
}

No método Main declaramos um novo enum TipoRenderezicao e atribuímos o valor TipoRenderezicao.ContentPage, da mesma forma que em um enum normal.

Observe que o operador "|=" adiciona um sinalizador ao enum, de modo que o enum agora contém dois bits de sinalizador.

A seguir vamos definir o método Verifica() onde vamos fazer a verificação das enumerações usando Flags:

static void Verifica(TipoRenderezicao tipo)
{
  switch (tipo)
  {
       case TipoRenderezicao.ContentPage | TipoRenderezicao.DataUri | TipoRenderezicao.GZip:
       {
                Console.WriteLine("content, datauri, gzip");
                break;
       }
       case TipoRenderezicao.ContentPage | TipoRenderezicao.GZip: // primeira ocorrencia
       {
                  Console.WriteLine("content, gzip");
                  break;
       }

       case TipoRenderezicao.ContentPage:
       {
                Console.WriteLine("content");
                break;
       }
       case TipoRenderezicao.ViewPage:
       {
                Console.WriteLine("view");
                break;
       }
       case TipoRenderezicao.HomePage | TipoRenderezicao.GZip:
       {
               Console.WriteLine("home, gzip");
               break;
       }
       case TipoRenderezicao.HomePage:
       {
              Console.WriteLine("home");
              break;
       }
}

O método Verifica mostra como ativar os sinalizadores de enum. Isso é útil pois permite que você atue em combinações de sinalizadores em uma expressão de tempo constante. Aqui o uso do operador "|" indica que os valores são combinados.

O Bitwise OR, usado aqui e representando por |, e retorna 1 no bit se algum dos valores estiver definido. Isso significa que ele pode ser usado no bloco switch, definindo todos os bits nas expressões "|".

Resultado da execução:

Vejamos outro exemplo que mostra a diferença de comportamento quando usamos ToString() para obter o valor de uma combinação em uma enumeração usando Flags.

Abaixo temos um exemplo onde definimos duas enumerações com o mesmo valor. A primeira usando uma enumeração normal e a segunda usando o atributo [Flags].

using System;

namespace EnumFlags2
{
    enum Valores { Baixo = 1, Medio = 2, Alto = 4, Maximo = 8 }

    [Flags]
    enum ValoresFlags { Baixo = 1, Medio = 2, Alto = 4, Maximo = 8 }

    class Program
    {
        static void Main(string[] args)
        {
            string iValor = (Valores.Baixo | Valores.Alto ).ToString();

            string fValor = (ValoresFlags.Baixo | ValoresFlags.Alto).ToString();

            Console.WriteLine("Valores obtidos com ToString em enumerações\n");
           
            Console.WriteLine($"enum normal : {iValor} usando [Flags] :  {fValor}");

            Console.ReadLine();
        }
    }
}

O resultado da execução é o seguinte:

Observe o resultado obtido quando usamos Flags.

O atributo Flags usado em enum representam constantes e combinações mais complexas onde usamos sinalizadores bit a bit com enums e podemos atribuir, adicionar, definir, verificar e ativar um enum com o atributo Flags.

Pegue o projeto aqui: EnumFlags.zip

"Amo ao SENHOR, porque ele ouviu a minha voz e a minha súplica.
Porque inclinou a mim os seus ouvidos; portanto, o invocarei enquanto viver."
Salmos 116:1,2

Referências:


José Carlos Macoratti