An enumeration is a declaration of named numerical constants. It is an often used concise solution for implementing discrete values and it keeps source code readable and type safe. Unfortunately, enumeration values cannot be categorized by default.
Enumeration values cannot be categorized without affecting the composition of the enumeration or writing source code for filtering the enumeration values. Let me explain this by an example (demo1).
In this demo I have created an enumeration with food items which are written to the console output using a simple foreach loop.
But what if we only want to show the Italian, Mexican or vegetarian food items?
We can split the current enumeration into separate enumerations, but when we need combined food items we would have to make new enumerations (which means that enumeration values will have to be defined in multiple enumerations) or implement a custom method with business logic for all enumeration values (which must be changed in case the enumeration changes).
In this blog I will provide a reusable solution for maintaining a single enumeration which can be categorized.
Source code is created using Visual Studio 2019, runs on .NET Core 2.2. and can be found on GitHub.
Attribute base class
First, we need to add categories to the enumeration values. Categories can be added in a declarative way by using attributes. In the next demo (demo2), we start by defining an Italian, Mexican and Vegetarian custom attribute class. Each class is derived from the Attribute base class. After we have defined these attribute classes, we can add them to the enumeration values.
Now we must retrieve the enumeration values based on a given attribute class. This is done by the GetEnumValues method. In this method, the System.Reflection namespace is used for retrieving the MemberInfo of each enumeration value. The MemberInfo contains member metadata and is passed into the Attribute.IsDefined method to check whether the MemberInfo contains the requested attribute class type. If so, the enumeration value is added to a list of enumeration values that is returned as output of the GetEnumValues method.
The Main method calls the GetEnumValues method for each type of food to generate the console output.
Because the output of the GetEnumValues method is a list, LINQ functionality can be applied if needed.
The solution we have seen so far is working fine, but it only works for our Food enumeration. This is where generics comes in.
For this demo (demo3), I have first restructured the project; enumerations in Enums.cs, categories in EnumAttributes.cs, GetEnumValues method in Utils.cs and Program.cs for writing the console output. I have also added the AttributeUsage attribute with value AttributeTargets.Field to the categories to minimize misuse. And although enumerations and categories are in separate files, they are both part of the Enums partial class. The main difference here is the implementation of generics for the GetEnumValues method.
The GetEnumValues method must be called using two class types; a type called TEnum for the source enumeration type and a type called TAttrib for the filter attribute class type. The where clauses prevent the usage of other types. The TEnum and TAttrib types are used with the System.Reflection namespace in the same way as in previous demo. The output of the GetEnumValues method is a list of enumeration values of the same type as the source enumeration type.
For testing purposes, an enumeration called Car has been added with the Supercar attribute.
With all this in place, the Main method can now call the GetEnumValues method for different enumeration types and attribute class types.
And that’s it: a reusable solution for maintaining a single enumeration which can be categorized by using the Attribute base class, System.Reflection and generics.