C# - Enum Extensions and Multiple Attributes

Viewed 55

I am writing enum class in c# that would hold several values and each of them will have several attributes. It is unlikely that any part of this values/attributes will change in the near future.

First I tried to use enum attributes, but I stumbled upon the fact that I don't need one but at least three different descriptions for each value. So I have used enum value for one attribute and description for another, but for 3rd and 4th I wrote methods that doesn't look very transparent or maintainable.

Is there a way to define 3rd and 4th attribute in the enum description also?

Here is the code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HtmlRefactoringConsole
{

    public enum MagazineType2
    {
        [Description("RRiF")]
        RRiF = 1,
        [Description("Pravo i porezi")]
        PiP = 3,
        [Description("Proračuni")]
        Proracuni = 8,
        [Description("Neprofitni")]
        Neprofitni = 7,
        [Description("Godišnji obračun")]
        GodisnjiObracun = 20,
        [Description("Obrtnici")]
        Obrtnici = 11,
        [Description("Obavijesti")]
        Obavijesti = 10
    }

    public static class MagazineType2Extensions
    {
        public static string GetLeadChar(this MagazineType2 magazineType)
        {
            if (magazineType == MagazineType2.GodisnjiObracun)
            {
                return "P";
            }
            return magazineType.ToString().Substring(0, 1);
        }

        public static int GetID(this MagazineType2 magazineType)
        {
            return (int)magazineType;
        }

        public static string GetLabel(this MagazineType2 magazineType)
        {
            switch (magazineType)
            {
                case MagazineType2.RRiF:
                    return "RRiF";
                case MagazineType2.PiP:
                    return "PiP";
                case MagazineType2.Proracuni:
                    return "Pror";
                case MagazineType2.Neprofitni:
                    return "Nepr";
                case MagazineType2.GodisnjiObracun:
                    return "PrGO";
                case MagazineType2.Obrtnici:
                    return "Obrt";
                case MagazineType2.Obavijesti:
                    return "Obav";
                default:
                    throw new ArgumentException("Item not found.", nameof(magazineType));
            }
        }

        public static string GetName(this MagazineType2 magazineType)
        {
            var field = magazineType.GetType().GetField(magazineType.ToString());
            if (Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) is DescriptionAttribute attribute)
            {
                return attribute.Description;
            }
            throw new ArgumentException("Item not found.", nameof(magazineType));
        }
    }
}

Then I decided to use more traditional approach and I have made this version:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HtmlRefactoringConsole
{

     public enum MagazineType
    {
        RRiF = 0,
        PiP = 1,
        Proracuni = 2,
        Neprofitni = 3,
        GodisnjiObracun = 4,
        Obrtnici = 5,
        Obavijesti = 6
    }

    public static class MagazineTypeExtensions
    {
        private struct MagazineData
        {
            public string LeadChar;
            public int ID;
            public string Label;
            public string Name;
        }

        // This values are required by external business rules

        private static MagazineData[] magazineData = new MagazineData[]
        {
            new MagazineData { LeadChar = "R", ID = 01, Label="RRiF", Name ="RRiF" },
            new MagazineData { LeadChar = "P", ID = 03, Label="PiP",  Name ="Pravo i porezi" },
            new MagazineData { LeadChar = "P", ID = 08, Label="Pror", Name ="Proračuni" },
            new MagazineData { LeadChar = "N", ID = 07, Label="Nepr", Name ="Neprofitni" },
            new MagazineData { LeadChar = "P", ID = 20, Label="PrGO", Name ="Godišnji obračun" },
            new MagazineData { LeadChar = "O", ID = 11, Label="Obrt", Name ="Obrtnici" },
            new MagazineData { LeadChar = "O", ID = 10, Label="Obav", Name ="Obavijesti" }
        };

        // file name leading char for this type of magazine

        public static string GetLeadChar(this MagazineType magazineType)
        {
            return magazineData[(int)magazineType].LeadChar;
        }

        public static int GetID(this MagazineType magazineType)
        {
            return magazineData[(int)magazineType].ID;
        }

        public static string GetLabel(this MagazineType magazineType)
        {
            return magazineData[(int)magazineType].Label;
        }

        public static string GetName(this MagazineType magazineType)
        {
            return magazineData[(int)magazineType].Name;
        }
    }
}

Both version yield identical results (the first one has two exceptions, but they will certainly never be called because the value of the enum will always be defined and valid).

I am in favor of the 2nd version because it is much more readable, but I wonder if it breaks some good code writing practices?

Usage sample for both versions:

foreach (MagazineType magazineType in Enum.GetValues(typeof(MagazineType)))
{
    Console.WriteLine($">{magazineType}<   >{magazineType.GetLeadChar()}<   >{magazineType.GetID()}<   >{magazineType.GetLabel()}<   >{magazineType.GetName()}<");
}

foreach (MagazineType2 magazineType in Enum.GetValues(typeof(MagazineType2)))
{
    Console.WriteLine($">{magazineType}<   >{magazineType.GetLeadChar()}<   >{magazineType.GetID()}<   >{magazineType.GetLabel()}<   >{magazineType.GetName()}<");
}
0 Answers
Related