6 maneiras de fazer a mesma coisa, o que é considerado boas práticas?

As vezes tem tantas maneiras diferentes de fazer o mesmo código que nós ficamos na dúvida quanto a qual maneira usar. O que seria considerado "boa prática" pela comunidade e o que sua equipe entenderia melhor. Suponhamos que você esteja trabalhando dentro de um método de um Domain Service chamado UmDomainServiceChique(objetoDoDominio) que será chamado por uma API. Você tem uma regra de negócio chique para ser verificada que por enquanto chamarei de VerificaMinhaRegraChiqueComplexa(). Você chama UmDomainServiceChique(objetoDoDominio) e caso VerificaMinhaRegraChiqueComplexa() retorne true você vai querer que UmDomainServiceChique faça o que tem que fazer e a api retornar Ok 200, caso contrário você quer que a API responda um erro qualquer, tipo BadRequest, e retornar uma mensagem dizendo que VerificaMinhaRegraChiqueComplexa deu ruim. Eu vejo 6 maneiras de fazer isso, gostaria de saber a opinião de outrs devs sobre qual seria a maneira menos gambiarr

Formatando um item ou membro de enum para texto amigável

Muitas vezes nós criamos Enums para expressar possíveis opções/valores de um campo de múltipla escolha onde os itens são fixos do sistema e não cadastrados em uma tabela.
Há casos em que os itens definem regras de negócio e rumos a tomar na aplicação. Nesses casos usar enum não é a melhor opção.
Em todos os outros casos usar enum pode ser legal, mas existe um problema: como popular um combo box / dropdown list com os itens do combo com nomes amigáveis? Sim, você não pode colocar espaços e caracteres especiais em um enum. Mas ao popular as opções, seja em uma aplicação windows forms ou web forms, você pode querer mostrar descrições longas e cheias de acentos e caracteres especiais para as opções no dropdown.
Você pode usar os atributos Description ou EnumMember para definir programaticamente essas descrições longas, mas obter esses valores de volta vai algumas linhas de código.
Nesse código que eu compartilho abaixo escrevi um extension method (não abuse deles) para o tipo enum chamado ToText() que tenta obter o Description, se não existir tenta obter o EnumMember, e se não existir eu criei um outro, chamado text, para exemplificar como podemos adicionar a qualquer membro do código propriedades e valores declarativamente através de atributos.
Você pode adaptar outros atributos, ou separar Description, EnumMember e Text em métodos separados se desejar.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.ComponentModel;
using System.Runtime.Serialization;

namespace VtrFramework.Extensions
{
    /// 
    /// classe que permite usar um atributo texto em um item de enum, para ele ter uma descrição amigável
    /// 
    public class TextAttribute : Attribute
    {
        public string Text;
        public TextAttribute(string text)
        {
            Text = text;
        }
    }

    /// 
    /// Adiciona um método a mais em um  elemento do tipo enum para pegarmos seu texto amigável
    /// 
    public static class EnumExtensions
    {

        /// 
        /// Dado um enum, retorna seu correspondente string que pode ser: "" se ele for null, Seu decorator/atributo Text se houver, ToString caso contrário
        /// funciona também com o atributo Description e com o atributo EnumMember
        /// 
        /// O Enum a ser convertido
        /// string - O texto do enum
        public static string ToText(this Enum enumeration)
        {

            if (enumeration == null)
            {
                return "";
            }

            MemberInfo[] memberInfo = enumeration.GetType().GetMember(enumeration.ToString());

            if (memberInfo != null && memberInfo.Length > 0)
            {
                try
                {
                    object[] descrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

                    if (descrs != null && descrs.Length > 0)
                    {
                        return ((DescriptionAttribute)descrs[0]).Description;
                    }


                    object[] enumbs = memberInfo[0].GetCustomAttributes(typeof(EnumMemberAttribute), false);

                    if (enumbs != null && enumbs.Length > 0)
                    {
                        return ((EnumMemberAttribute)enumbs[0]).Value;
                    }

                    object[] txts = memberInfo[0].GetCustomAttributes(typeof(TextAttribute), false);

                    if (txts != null && txts.Length > 0)
                    {
                        return ((TextAttribute)txts[0]).Text;
                    }
                }
                catch
                {
                    //catch mudinho porque o fallback é direto para o ToString
                }

            }

            return enumeration.ToString();

        }



    }
}


Use-o assim:

   
 namespace VtrFramework.Test.Extensions
{
    [TestFixture, Description("Teste de métodos de extensão para enuns"), Category("ExtensionMethods")]
    public class EnumExtensionsTest
    {
        public enum TargetEnum
        {
            [System.ComponentModel.Description("Opção 1")]
            Opcao1,

            [EnumMember(Value ="Opção 2")]
            Opcao2,

            [Text("Opção 3")]
            Opcao3,

            Opcao4
        }

        [Test]
        [Description("Teste de método que traz descrição amigável do enum")]
        [Category("ExtensionMethods")]
        public void ToTextTest()
        {
            Assert.AreEqual("Opção 1", TargetEnum.Opcao1.ToText());
            Assert.AreEqual("Opção 2", TargetEnum.Opcao2.ToText());
            Assert.AreEqual("Opção 3", TargetEnum.Opcao3.ToText());
            Assert.AreEqual("Opcao4", TargetEnum.Opcao4.ToText());
        }
    }
}

Você pode popular listbox e dropdowns assim:
    
  /// <summary>
    /// Ferramentas para trabalhar com Enuns
    /// </summary>
    public class EnumTools
    {
        /// <summary>
        /// publica os itens de um enum em um ListItemCollection
        /// </summary>
        /// <typeparam name="TEnum">O tipo do enum a ser publicado</typeparam>
        /// <param name="lst">System.Web.UI.WebControls.ListItemCollection da ListBox ou DropDownList que vai ser preenchida</param>
        /// <param name="primeiroVazio">true se o primeiro item for o vazio/default, false caso contrário</param>
        /// <param name="textoPrimeiroVazio">texto do primeiro item, que não deve fazer parte do enum</param>
        /// <param name="valorPrimeiroVazio">valor do primeiro item, que pode ser String.Empty, 0 ou null dependendo da regra</param>
        public static void PublicaEnum<TEnum>(System.Web.UI.WebControls.ListItemCollection lst, bool primeiroVazio = true, string textoPrimeiroVazio = "Escolha uma opção", string valorPrimeiroVazio = "") where TEnum : struct, IConvertible
        {
            if (!typeof(TEnum).IsEnum)
            {
                throw new ArgumentException("TEnum precisa ser do tipo ENUM");
            }

            lst.Clear();
            if (primeiroVazio)
            {
                lst.Insert(0, new System.Web.UI.WebControls.ListItem(textoPrimeiroVazio, valorPrimeiroVazio));
            }

            var valores = Enum.GetValues(typeof(TEnum));

            foreach(var v in valores)
            {
                string text = ((Enum)v).ToText();
                string value = ((int)v).ToString();
                lst.Add(new System.Web.UI.WebControls.ListItem(text, value));
            }
        }

        /// <summary>
        /// publica os itens de um enum em um DropDownList
        /// </summary>
        /// <typeparam name="TEnum"></typeparam>
        /// <param name="drop">o DropDown/ComboList a ser preenchido</param>
        /// <param name="primeiroVazio">true se o primeiro item for o vazio/default, false caso contrário</param>
        /// <param name="textoPrimeiroVazio">texto do primeiro item, que não deve fazer parte do enum</param>
        /// <param name="valorPrimeiroVazio">valor do primeiro item, que pode ser String.Empty, 0 ou null dependendo da regra</param>
        public static void PublicaEnum<TEnum>(System.Web.UI.WebControls.DropDownList drop, bool primeiroVazio = true, string textoPrimeiroVazio = "Escolha uma opção", string valorPrimeiroVazio = "") where TEnum : struct, IConvertible
        {
            PublicaEnum<TEnum>(drop.Items, primeiroVazio, textoPrimeiroVazio, valorPrimeiroVazio);
        }

        /// <summary>
        /// publica os itens de um enum em um RadioButtonList
        /// </summary>
        /// <typeparam name="TEnum">O tipo do enum a ser publicado</typeparam>
        /// <param name="rbl">o RadioButtonList a ser publicado</param>
        public static void PublicaEnum<TEnum>(System.Web.UI.WebControls.RadioButtonList rbl) where TEnum : struct, IConvertible
        {
            PublicaEnum<TEnum>(rbl.Items, false);
        }

        /// <summary>
        /// publica os itens de um enum em um ListBox
        /// </summary>
        /// <typeparam name="TEnum">O tipo do enum a ser publicado</typeparam>
        /// <param name="lbx">o ListBox a ser publicado</param>
        public static void PublicaEnum<TEnum>(System.Web.UI.WebControls.ListBox lbx) where TEnum : struct, IConvertible
        {
            PublicaEnum<TEnum>(lbx.Items, false);
        }

    }
Isso ajuda bastante na hora de criarmos listas com as UF's do Brasil, por exemplo. Veremos isso num próximo post.

Comentários

Postagens mais visitadas deste blog

Busca de CEP com o Lazarus - Parte 1 - UrlEncode

Botão Add This para adicionar seu post em qualquer rede

Uso de memória no SQL Server