Acesso Eficiente a Atributos Personalizados em Valores de Enum em C#

Se você está desenvolvendo aplicações em C#, pode, em alguns momentos, encontrar a necessidade de buscar atributos personalizados associados a valores de enum. Essa tarefa pode parecer simples, especialmente para aqueles que não estão familiarizados com reflexão e geração de código IL. No entanto, converter valores de enum em strings (seus nomes) pode muitas vezes resultar em problemas de desempenho. Neste post, exploraremos uma maneira limpa e eficiente de recuperar esses atributos sem a complicação de conversões de string.

Compreendendo o Problema

Vamos começar com um exemplo rápido onde definimos um enum adornado com atributos personalizados:

public enum MyEnum {
    [CustomInfo("Este é um atributo personalizado")]
    None = 0,

    [CustomInfo("Este é outro atributo")]
    ValueA,

    [CustomInfo("Este tem uma bandeira extra", AllowSomething = true)]
    ValueB,
}

Neste trecho, temos um enum chamado MyEnum, que contém vários valores, cada um emparelhado com um atributo CustomInfo detalhando informações adicionais relacionadas a cada membro do enum.

O Desafio

O principal desafio surge quando precisamos recuperar esses atributos CustomInfo de uma instância do enum. O método inicial que vem à mente é usar o seguinte código:

public CustomInfoAttribute GetInfo(MyEnum enumInput) {
    Type typeOfEnum = enumInput.GetType();
    FieldInfo fi = typeOfEnum.GetField(enumInput.ToString());
    return fi.GetCustomAttributes(typeof(CustomInfoAttribute), false).
        FirstOrDefault() as CustomInfoAttribute;
}

Embora esta função recupere eficazmente o atributo, ela sofre de ineficiências de desempenho devido ao uso de enumInput.ToString(), que converte o valor do enum em sua representação em string.

Uma Abordagem Melhor: Usando Geração de Código IL

Felizmente, existe um método mais eficiente para acessar atributos personalizados sem converter enums em strings, utilizando a geração de código IL. Ao empregar métodos dinâmicos e o ILGenerator, reduzimos a sobrecarga e melhoramos o desempenho. Abaixo, delineamos como criar um delegador que ajuda no acesso rápido às propriedades, que pode ser adaptado para a recuperação de atributos também.

Passos para Implementar Geração de Código IL

  1. Criar um Delegado: Defina um delegador que recebe um objeto e retorna um objeto. Isso nos permitirá criar um acessador de propriedade rápido.

    public delegate object FastPropertyGetHandler(object target);
    
  2. Emitir IL: Usando o ILGenerator, podemos gerar um método IL que carrega o objeto alvo na pilha, chama o método getter e trata o valor de retorno adequadamente.

    private static void EmitBoxIfNeeded(ILGenerator ilGenerator, System.Type type) {
        if (type.IsValueType) {
            ilGenerator.Emit(OpCodes.Box, type);
        }
    }
    
  3. Obter Getter de Propriedade: Agora, criamos um método para gerar um método dinâmico e construir o código IL necessário para criar um acessador de propriedade rápido:

    public static FastPropertyGetHandler GetPropertyGetter(PropertyInfo propInfo) {
        DynamicMethod dynamicMethod = new DynamicMethod(
            string.Empty, 
            typeof(object), 
            new Type[] { typeof(object) },
            propInfo.DeclaringType.Module);
    
        ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
        ilGenerator.Emit(OpCodes.Ldarg_0);
        ilGenerator.EmitCall(OpCodes.Callvirt, propInfo.GetGetMethod(), null);
        EmitBoxIfNeeded(ilGenerator, propInfo.PropertyType);
        ilGenerator.Emit(OpCodes.Ret);
        FastPropertyGetHandler getter = (FastPropertyGetHandler)dynamicMethod.CreateDelegate(typeof(FastPropertyGetHandler));
        return getter;
    }
    

Conclusão

Ao implementar o método descrito acima, você pode reduzir efetivamente a sobrecarga associada à recuperação de atributos de valores de enum. Essa abordagem não apenas elimina conversões de string desnecessárias, mas também produz um desempenho de execução mais rápido—essencial para aplicações que exigem eficiência.

Incorporar essas técnicas em seus projetos C# ajudará você a navegar suavemente pelos atributos de enum e permitirá um código mais limpo e manutenível. Boa codificação!