Click here to Skip to main content
15,893,622 members
Articles / Programming Languages / C#

Smart enums in C#

Rate me:
Please Sign up or sign in to vote.
4.43/5 (24 votes)
6 Aug 2017CPOL1 min read 24.9K   191   25   7
Replacement for standard enums with additional functionality.

Introduction

Long time ago I've learned from John Skeet's blog about java enums which are full-fledged classes. This approach have obvious benefits: you can add behavior to enums.

I've found on Stackoverflow solutions how to implement this in C# . Unfortunately all of them haven't worked with switch statement because case requires to be followed by constant value which can't be of reference type (string is the only exception with special support from the compiler).

Things have changed with C# 7.0 with new enhanced switch statement.

Using the code

Basic idea is to change enum to an immutable class and expose static readonly instances of that class.

Most of boilerplate code I've placed in EnumBase class. This includes serialization support, comparison operators, etc. All my enums inherit from this class.

In EnumBase class I've assumed that internally enum values are represented by int. This class also supports enum description which in standard enum is usually represented by some custom attribute.

As an example here is TransactionState class:

public class TransactionState : EnumBase
{
    public static readonly TransactionState COMPLETE = new TransactionState(1, "Transaction complete");
    public static readonly TransactionState REJECTED = new TransactionState(2, "Transaction rejected");
    public static readonly TransactionState PENDING = new TransactionState(3, "Transaction pending");
    public static readonly TransactionState AWAITING_APPROVAL = new TransactionState(4, "Transaction awaiting approval");

    private TransactionState(int value, string description)
        : base(value, description)
    {
    }

    private TransactionState(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
    }

    public virtual bool IsTransactionComplete()
    {
        if (value == 1)
            return true;

         return false;
    }
}

This example is based on java sample from this blog: http://blog.scottlogic.com/2016/07/28/java-enums-how-to-use-them-smarter.html

As you see there is very little boilerplate code - just one additional constructor for proper serialization and another one to initialize values.

IsTransactionComplete is a method which adds some useful logic inside enum itself: it decides about enum category. With standard enums you would have to implement this in some other helper class.

Now is the time to show usage of this class:

C#
class Program
{
    static void Main()
    {
        TransactionState value = TransactionState.PENDING;
        TransactionState secondValue = TransactionState.COMPLETE;

        // Use switch with pattern matching.
        switch (value)
        {
            case var tsValue when tsValue == TransactionState.COMPLETE && secondValue == TransactionState.AWAITING_APPROVAL:
                Console.WriteLine("Y");
                break;
            case var tsValue when tsValue == TransactionState.PENDING && secondValue == TransactionState.COMPLETE:
                Console.WriteLine("Value is PENDING, secondValue is COMPLETE");
                break;
            case var tsValue when tsValue == TransactionState.REJECTED:
                Console.WriteLine("Z");
                break;
        }

        if (value.IsTransactionComplete())
        {
            Console.WriteLine("Value is COMPLETE");
        }
        else
        {
            Console.WriteLine("Value is not COMPLETE");
        }

        Console.WriteLine("value.Tostring(): {0}", value.ToString());
    }
}
I've used new switch form with pattern matching and when keyword.

Conclusion

Since C# 7.0 you can use standard classes in place of enums which can be useful for greater flexibility and usability in more complex cases.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Poland Poland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionExtension methods Pin
Eric Lynch17-Nov-18 4:53
Eric Lynch17-Nov-18 4:53 
Questioncode availability? Pin
Member 85555501-Sep-17 5:43
Member 85555501-Sep-17 5:43 
QuestionWhere is the download link? Pin
James Curran1-Aug-17 7:51
James Curran1-Aug-17 7:51 
AnswerRe: Where is the download link? Pin
robsosno6-Aug-17 8:10
robsosno6-Aug-17 8:10 
AnswerRe: Where is the download link? Pin
robsosno6-Aug-17 8:43
robsosno6-Aug-17 8:43 
QuestionThe serialization is quite odd. Pin
Paulo Zemek31-Jul-17 11:23
mvaPaulo Zemek31-Jul-17 11:23 
Hi. I think the idea is quite good, but the serialization thing is quite odd.
To me, the fact that the constructor is private means that there should only exist the instances declared on the class itself.
Yet, by supporting serialization and deserialization the wrong way, new instances might be created. Even worse, if I serialize an item with an old description, change the description in the source code and deserialize the item, I get the deserialized instance with the old description.

I would say that serialization should only serialize the enum value and deserialization would need to be able to get the right enum instance instead of creating a new one.

I know this is a bit tricky with BinaryFormatter, but when serializing you can change the type that's going to be deserialized by setting the serializationInfo.FullTypeName. I am not sure if you need to set it to a ISerializationSurrogate or to another type though (honestly, I avoid BinaryFormatter for many than 5 years now).
In any case, I would prefer it to not support serialization at all (and if needed I would use a serializer more capable) than to support it allowing new instances of the enums to be created. I would also implement keep the equals doing ReferenceEquals.

AnswerRe: The serialization is quite odd. Pin
robsosno6-Aug-17 9:07
robsosno6-Aug-17 9:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.