.NET Enumerations: Generic Constraints and Custom Values

Usually whenever I write something semi-cool at work, I get home and tell my flatmate “Hey – check out this semi-cool thing I did today”. He replies “Yeah – that’s cool – you should blog it” to which I respond “Hmmm … maybe later – I’m hungry”. Well, here’s the first post from what is hopefully a series of semi-cool things Tatham did at work one day.

Many of these posts will cover very simple concepts, but they are concepts or code snippets that often get skipped over and thus just aren’t that well documented around the web.


One of the things that occassionally bugs me is that I can’t use a string as a base type for an enumeration. I’m sure there’s a good underlying reason, but when working with existing systems it’d be nice to write something like this:

using FuelAdvance.Components.Common;

namespace FuelAdvance.Components.Payments.Gateways.PayPal
{
    public enum PaymentStatus : string
    {
        CanceledReversal = "Canceled-Reversal",
        Completed = "Completed",
        Denied = "Denied",
        Expired = "Expired",
        Failed = "Failed",
        InProgress = "In-Progress",
        PartiallyRefunded = "Partially-Refunded",
        Pending = "Pending",
        Processed = "Processed",
        Refunded = "Refunded",
        Reversed = "Reversed",
        Voided = "Voided",
        Unknown = "Unknown"
    }
}

Well, .NET doesn’t work that way so behold FuelAdvance.Components.Common.EnumerationHelpers.

Now, we can write our enumeration like this:

using FuelAdvance.Components.Common;

namespace FuelAdvance.Components.Payments.Gateways.PayPal
{
	public enum PaymentStatus
	{
		[Value("Canceled-Reversal")] CanceledReversal,
		Completed,
		Denied,
		Expired,
		Failed,
		[Value("In-Progress")] InProgress,
		[Value("Partially-Refunded")] PartiallyRefunded,
		Pending,
		Processed,
		Refunded,
		Reversed,
		Voided,
		Unknown
	}
}

Really, the code is nicer as we only need to include strings for the three values which include dashes. The Value attribute is about a basic as attributes get, and it doesn’t need to be any more complex.

The EnumerationHelpers class provides the logic for mapping string values to enumeration values and back again:

PaymentStatus status = EnumerationHelpers.GetEnumValue<PaymentStaus>("In-Progress")

In writing the EnumerationHelpers class, I discovered a rather annoying limitation of generic constraints – you can’t constrain to an enum like so:

public static T GetEnumValue<T>(string value) where T : enum
{
    ...
}

Instead, you can only narrow the field as far as a struct, then test the type manually:

public static T GetEnumValue<T>(string value) where T : struct
{
    if (!typeof(T).IsEnum) throw ExceptionHelpers.NewArgumentNotAnEnumException(typeof(T).FullName);
    ...
}

UPDATE: I received an email this morning from Sonny Malhi saying “I’m not sure of the complete intent of the post ‘.NET Enumerations: Generic Constraints and Custom Values’ but if it is to return a description based on a title I believe you should use the [description] attribute if your goign to use a enum type. This way you get a name, value and description from your enum type.

The point of the [Value] attribute is not for UI display – you would still use the builtin [Description] attribute for that. The usefulness of the [Value] attribute is when you’re trying to integrate with an existing API or data source. You’ll notice that the example above is a PaymentStatus enumeration from my PayPal components namespace. PayPal return values from their API such as “In-Progress” which aren’t valid as enumeration item names, and I wanted to avoid writing a ParsePaymentStatus(string) method which just contained a massive switch statement.


You can download the latest version of all the code related to this post from our SVN repository here:

If you find a bug, please email me, or better yet, email me the patch.

One thought on “.NET Enumerations: Generic Constraints and Custom Values

Comments are closed.