Write Clean and Fast with C# Extensions

A frequent developer issue that arises with the use of enumerators is that the value ends up having to be used as part of a string of human-readable text, be it a graphic UI, a web page, or log file.

For example, a typical enumerator define in code might look something like this:

public enum ProcessingState
{
    LoadLocations,
    GeneratePaths,
    GenerateRoutes,
    CalculateFuelCosts
}

Displaying human readable text for a variable of this enumerator often results in static switch statement lookup methods such as this:

public static string GetProcessingStateText(ProcessingState state)
{
    switch (state)
    {
        case ProcessingState.LoadLocations:
            return "Loading locations";
        case ProcessingState.GeneratePaths:
            return "Generating paths";
        case ProcessingState.GenerateRoutes:
            return "Generating routes";
        case ProcessingState.CalculateFuelCosts:
            return "Calculating fuel costs";
        default:
            return state.ToString();
    }
}

Then each time in code where a display string is constructed, a lengthy method call is embedded in:

string output = "Processing State: " + PathGenerator.GetProcessingStateText(state);

As the project grows, it becomes increasing cumbersome to manage changes. New values may be added to the enumerator type, and you may forget to update the text lookup method. And, storing string in language specific resources can provide only minimal relief.

A new, and often overlooked C# language feature introduced with C# 3.0 of Extensions methods, allow for greatly simplifying this process with far less code to maintain. The extension method allows adding a new method to a type without creating a derived class or modifying the original type. This includes .NET core types such as String or Enum. Below is an example that defines a new extension method called DisplayName to the Enum type. It is recognized as an extension method by the parameter because it has “this” followed by the type being extended, then by a parameter value. The parameter value is a reference to the instance of the extended object when the method is used.

using System;
using System.Reflection;
public static class EnumExtensions
{
    public static string DisplayName(this Enum value)
    {
        FieldInfo field = value.GetType().GetField(value.ToString());
        EnumDisplayNameAttribute attribute = Attribute.GetCustomAttribute(field, typeof(EnumDisplayNameAttribute))
                    as EnumDisplayNameAttribute;
        return attribute == null ? value.ToString() : attribute.DisplayName;
    }
}

The method itself simply looks for the existence of an attribute on the Enum value called DisplayNameAttribute and, if found, returns value of the DisplayName property. If the attribute is not found it falls back to returning the value name.

This simple attribute is defined as follows:

public class EnumDisplayNameAttribute : Attribute
{
    public string DisplayName { get; set; }
}

Please note that there is no actual required correlation between the two uses of DisplayName. It is simply to make the association easier to track.

Now with the new attribute defined, the original ProcessingState enumerator definition can be changed to add human readable descriptions directly to each enumerator value, putting both the value and the human readable text for the value together. The GetProcessingStateText method can be eliminated.

public enum ProcessingState
{
    [EnumDisplayName(DisplayName = "Loading locations")]
    LoadLocations,
    [EnumDisplayName(DisplayName = "Generating paths")]
    GeneratePaths,
    [EnumDisplayName(DisplayName = "Generating routes")]
    GenerateRoutes,
    [EnumDisplayName(DisplayName = "Calculating fuel costs")]
    CalculateFuelCosts
}

Now, to use the extension method, simply call it like any other, changing the earlier example for creating the output string variable like this:

string output = "Processing State: " + state.DisplayName();

The advantages of this approach now become clear:

  • Lookup methods of human readable text for enumerator value are eliminated, reducing code size. The extension method and attribute can be universally applied to any enumerator type.
  • Developers no longer need to stop, find, and reference such lookup methods, nor add additional using statements for the namespaces of them. They can simply use the extension method universally. Coding is easier and faster.
  • The human readable text is much easier to manage and maintain in code. Developers are modifying the enumerator type to see the presence of the attributes and immediately recognize the need to add or update the human readable text accordingly.
  • The display name attribute can be changed to implement multiple languages or be a key value to a language resource file all while keeping everything together in code within the enumerator type definition.

Have questions about coding using C# and it’s new extensions? Contact KTL Solutions via email at info@ktlsolutions.com or by calling our main line at 301.360.0001.