© 2019

Enums - Usando Enumeradores

Nesse artigo além do básico de se trabalhar com Enums explico como deixar os enumeradores ainda mais declarativos utilizando DescriptionAttribute

Um objeto Enum é um tipo de artefato de linguagens orientadas a objetos que permite padronizar valores com um determinado texto, como no exemplo abaixo:

1
2
3
4
5
6
7
8
9
10
11
12
13
public enum Perfil
{
    //Posto mais baixo do Exército brasileiro 
    Graduado = 1,
    //É o primeiro nível dos oficiais do exército 
    Subalterno = 2,
    //Nível acima dos subalternos
    Itermediario = 3,
    //São oficiais de nível superior em relação os demais níveis
    Superior = 4,
    //Classe mais alta do exército brasileiro
    General = 5
}

Sem enumeradores o código fica muito mais complexo e difícil de entender.
Veja o exemplo abaixo, o perfil deve ser testado com seus respectivos códigos:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class Program
{
    private static int perfil;

    static void Main(string[] args)
    {
        //Perfil 1 = Graduado - Posto mais baixo do Exército brasileiro 
        //Perfil 2 = Subalterno - É o primeiro nível dos oficiais do exercíto 
        //Perfil 3 = Intermediario - São oficiais de nível superior em relação os demais níveis
        //Perfil 4 = Superior - São oficiais de nível superior em relação os demais níveis
        //Perfil 5 = General - Classe mais alta do exército brasileiro

        Console.WriteLine("Digite o código de sua patente");
        int.TryParse(Console.ReadLine(),out perfil);

        if (perfil == 1)
        {
            Console.WriteLine("Graduado - Posto mais baixo do Exército brasileiro");
            return;
        }
        if (perfil == 2)
        {
            Console.WriteLine("Subalterno - É o primeiro nível dos oficiais do exercíto ");
            return;
        }
        if (perfil == 3)
        {
            Console.WriteLine("Intermediario - São oficiais de nível superior em relação os demais níveis ");
            return;
        }
        if (perfil == 4)
        {
            Console.WriteLine("Superior - São oficiais de nível superior em relação os demais níveis ");
            return;
        }
        if (perfil == 5)
        {
            Console.WriteLine("General - Classe mais alta do exército brasileiro ");
            return;
        }

        Console.WriteLine("Patente não reconhecida");
        return;


    }

    
}

Se não fosse pelos comentários o desenvolvedor não teria a mínima noção do que seriam esses códigos.
E pior, se o desenvolvedor precisar testar o perfil em outros arquivos e classes do programa ele terá que copiar os comentários para guiar futuros programadores que precisarem dar manutenção nesse código.

Agora veja o mesmo código utilizando o enum:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public enum Perfil
{
    //Posto mais baixo do Exército brasileiro 
    Graduado = 1,
    //É o primeiro nível dos oficiais do Exército 
    Subalterno = 2,
    //Nível acima dos subalternos
    Intermediario = 3,
    //Sáo oficiais de nível superior em relação os demais níveis
    Superior = 4,
    //Classe mais alta do Exército brasileiro
    General = 5
}

class Program
{
    private static Perfil perfil;

    static void Main(string[] args)
    {
        Console.WriteLine("Digite o código de sua patente");
        Enum.TryParse(Console.ReadLine(), out perfil);

        if (perfil == Perfil.Graduado)
        {
            Console.WriteLine("Graduado - Posto mais baixo do Exército brasileiro");
            return;
        }
        if (perfil == Perfil.Subalterno)
        {
            Console.WriteLine("Subalterno - É o primeiro nível dos oficiais do exercíto ");
            return;
        }
        if (perfil == Perfil.Intermediario)
        {
            Console.WriteLine("Intermediario - São oficiais de nível superior em relação os demais níveis ");
            return;
        }
        if (perfil == Perfil.Superior)
        {
            Console.WriteLine("Superior - São oficiais de nível superior em relação os demais níveis ");
            return;
        }
        if (perfil == Perfil.General)
        {
            Console.WriteLine("General - Classe mais alta do exército brasileiro ");
            return;
        }

        Console.WriteLine("Patente não reconhecida");
        return;

    }

O código é praticamente o mesmo, apenas foi substituido nas linhas 17 e 22 a utilização do enum Perfil para fazer a leitura do código de patente digitado.
E as linhas que comparam o valor digitado com o perfil, linhas: 24, 29, 34, 39 e 44 que não mais usam números e sim a descrição de cada patente.

Assim temos o benefício de tornar o código mais fácil de entender para o desenvolvedor que precisar dar manutenção no código.

A classe Enum

No exemplo anterior utilizamos a classe Enum para converter um número digitado em um objeto do tipo Perfil

1
Enum.TryParse(Console.ReadLine(), out perfil);

Além dessa possibilidade a classe Enum disponibliza diversas funcionalidades para tratarmos com maior facilidade os enumeradores. Por exemplo:

  • GetNames() - Retorna um string array dos enumeradores
  • GetValues() - Retorna um Enum array dos enumeradores

Esses são os principais, porém há outras funcionalidades que valem a pena serem conhecidas.

Mas com o GetNames() por exemplo é possível listar todos os Enumeradores do enum Perfil:

1
2
3
4
5
6
7
8
9
10
private static StringBuilder ListarEnums()
{
    var stringBuilder = new StringBuilder();
    foreach (var perfil in Enum.GetNames(typeof(PatenteV1.Perfil)))
    {
        stringBuilder.AppendLine(perfil);
    }

    return stringBuilder;
}

O código completo desse exemplo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public static class PatenteV1
{
    public enum Perfil
    {
        //Posto mais baixo do Exército brasileiro 
        Graduado = 1,
        //É o primeiro nível dos oficiais do Exército 
        Subalterno = 2,
        //Nível acima dos subalternos
        Intermediario = 3,
        //Sáo oficiais de nível superior em relação os demais níveis
        Superior = 4,
        //Classe mais alta do Exército brasileiro
        General = 5
    }
}

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Lista de perfis");
      
        Console.WriteLine(ListarEnums());
    }

    private static StringBuilder ListarEnums()
    {
        var stringBuilder = new StringBuilder();
        foreach (var perfil in Enum.GetNames(typeof(PatenteV1.Perfil)))
        {
            stringBuilder.AppendLine(perfil);
        }

        return stringBuilder;
    }
}

E como resultado temos

Lista de perfis
Graduado
Subalterno
Intermediario
Superior
General

Enums com Atributo Description


Agora que você já sabe o que é um Enum vamos deixar os Enums ainda mais claros para o desenvolvedor e o código ainda mais simples utilizando DescriptionAttribute.
No exemplo anterior criamos o enum assim:

1
2
3
4
5
6
7
8
9
10
11
12
13
public enum Perfil
{
    //Posto mais baixo do Exército brasileiro 
    Graduado = 1,
    //É o primeiro nível dos oficiais do Exército 
    Subalterno = 2,
    //Nível acima dos subalternos
    Intermediario = 3,
    //Sáo oficiais de nível superior em relação os demais níveis
    Superior = 4,
    //Classe mais alta do Exército brasileiro
    General = 5
}

O problema no código acima é que apesar dos niveis de perfil estarem bem descritos, o significado de cada nível ainda depende de comentário no código, por exemplo:

Graduado é o nível mais baixo do Exército
O programdor só sabe disso porque está escrito no comentário
Se solicitada uma tela para explicar o que é cada patente o programador teria que fazer exatamente como fizemos no exemplo anterior.
Porém, podemos deixar essas explicações serem parte do código utilizando DescriptionAttribute do namesapce ComponentModel, conforme o exemplo abaixo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System.ComponentModel;
public enum Perfil
{

    [Description("Posto mais baixo do Exército brasileiro ")]
    Graduado = 1,
    [Description("É o primeiro nível dos oficiais do Exército ")]
    Subalterno = 2,
    [Description("Nível acima dos subalternos")]
    Intermediario = 3,
    [Description("Sáo oficiais de nível superior em relação os demais níveis")]
    Superior = 4,
    [Description("Classe mais alta do Exército brasileiro")]
    General = 5
}

Note que as explicações deixaram de ser comentário e agora fazem parte do código, e através de uma função ExtensionMethod, podemos estender a classe Enum para receber um novo método chamado descrição, conforme abaixo:

1
2
3
4
5
6
7
8
9
10
11
public static class EnumExtensions
{
    public static string Descricao(this Enum enumRecebido)
    {
        var descriptionAttribute = enumRecebido.GetType()
            .GetMember(enumRecebido.ToString())[0]
            .GetCustomAttribute(typeof(DescriptionAttribute), false) as DescriptionAttribute;

        return descriptionAttribute is null ? enumRecebido.ToString() : descriptionAttribute.Description;
    }
}

Com essa nova extension então podemos chamar o método Descricao em cada um dos enums, conforme abaixo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 static void Main(string[] args)
  {
      Console.WriteLine(ListarDescricoesPerfis());
  }

  private static string ListarDescricoesPerfis()
  {
      var stringBuilder = new StringBuilder();
      foreach (Perfil perfil in Enum.GetValues(typeof(Perfil)))
      {
          stringBuilder.AppendLine($"{perfil.ToString()} - {perfil.Descricao()}");
      }

      return stringBuilder.ToString();
  }
  

Na linha 9 obtenho cada um dos perfis do enum Perfil, e na linha 11 concateno a nome do Perfil “perfil.ToString()” e a descrição “perfil.Descricao()” e o resultado fica conforme abaixo:

  • Graduado - Posto mais baixo do Exército brasileiro
  • Subalterno - É o primeiro nível dos oficiais do Exército
  • Intermediario - Nível acima dos subalternos
  • Superior - Sáo oficiais de nível superior em relação os demais níveis
  • General - Classe mais alta do Exército brasileiro
  • Master - Master

Entendendo a Extenision Method “Descricao()”


1
2
3
4
5
6
7
8
9
10
11
public static class EnumExtensions
{
    public static string Descricao(this Enum enumRecebido)
    {
        var descriptionAttribute = enumRecebido.GetType()
            .GetMember(enumRecebido.ToString())[0]
            .GetCustomAttribute(typeof(DescriptionAttribute), false) as DescriptionAttribute;

        return descriptionAttribute is null ? enumRecebido.ToString() : descriptionAttribute.Description;
    }
}

Note que na linha 3, o método descrição será injetado na classe Enum, então quando chamamos no exemplo anterior “perfil.Descricao()” o perfil será passado para essa função, na linha 5 através do GetType e GetMember obtemos o DescriptionAttribute e na linha 9 o description atribute do perfil recebido será retornado, e caso no enum não tenha sido declardo o DescritpionAttribute, será retornado o “enumRecebido.ToString()”

Conclusão


Trabalhar com Enumeradores, simplifica a manutenção do código, pois torna ele fácil de entendeder! Utilizar DescriptionAttributes nos enumeradores torna o código ainda mais simples e tira a necessidade de comentar o siginificado de cada Enumerador, fazendo que o significado também seja parte do código fonte.

Espero que tenham gostado!

Baixe os exemplos do meu git em: https://github.com/ricardovicentini/enums