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()}<");
}