Click here to Skip to main content
11,481,404 members (60,065 online)
Click here to Skip to main content

Turn an Enum into a Simple State Machine

, 6 Apr 2014 CPOL 4.5K 7
Rate this:
Please Sign up or sign in to vote.
Turn an Enum into a simple state machine via a generic wrapper and modular increment methods

Introduction

This tip describes a generic EnumWrapper class that exposes modular increment methods such as NextEnum, PrevEnum, and IncrementEnum. The intention is to make working with enums more convenient, especially when representing the state of a system.

Using the Code

Given an arbitrary enum of the form...

   public enum PlayState
    {
        Play,
        Stop,
        Pause,
        Options,
        HUD
    }  

...wrap it up by creating the instance:

    var playStateWrapper = new EnumWrapper<PlayState>();  

By default, the CurrentEnum property of playStateWrapper is set to PlayState(0) = Play. The methods in the EnumWrapper class are designed to modify this value in a modular fashion - in the sense of modular arithmetic, where in this case the modulus is the size of PlayState. The value of CurrentEnum is affected by the public methods NextEnum, PrevEnum, IncrementEnum, and SetEnum in the obvious ways. Moreover, the methods NextEnum, PrevEnum, IncrementEnum each have a boolean parameter that determines whether the CurrentEnum should be set to that value. Otherwise, they just return the enum's value. Since the EnumWrapper class is full of comments and I've included the entire class, you can see how it works.

using System;

namespace YourNameSpace
{
    /// <summary>
    /// Wraps up an enum and exposes increment methods
    /// </summary>
    public class EnumWrapper<T>
    {
        /// <summary>
        /// We store the current enum's index so we don't have to do a linear 
        /// search
        /// </summary>
        private int _CurrentEnumIndex;

        /// <summary>
        /// Backing field for the Enums property
        /// </summary>
        private readonly Lazy<T[]> _Enums;

        /// <summary>
        /// Returns the array of wrapped up enums 
        /// </summary>
        public T[] Enums
        {
            get
            {
                return _Enums.Value;
            }
        }

        /// <summary>
        /// Gets and sets the current enum. 
        /// </summary>
        public T CurrentEnum { get; private set; }

        /// <summary>
        /// Wraps up an enum and exposes increment methods
        /// </summary>
        public EnumWrapper()
        {
            _Enums = new Lazy<T[]>(() => { return (T[])Enum.GetValues(typeof(T)); });
        }

        /// <summary>
        /// Gets the next wrapped up enum. This is modular, so if the current enum is 
        /// the last enum, then this will return the first enum
        /// </summary>
        /// <param name="setToCurrent">If true, sets CurrentEnum = NextEnum</param>
        /// <returns>The next enum</returns>
        public T NextEnum(bool setToCurrent)
        {
            return IncrementedEnum(setToCurrent, 1);
        }

        /// <summary>
        /// Gets the previous wrapped up enum. This is modular, so if the
        /// current enum is the first enum, then this will return the last enum
        /// </summary>
        /// <param name="setToCurrent">If true, sets the CurrentEnum = PrevEnum</param>
        /// <returns>The previous enum</returns>
        public T PrevEnum(bool setToCurrent)
        {
            return IncrementedEnum(setToCurrent, -1);
        }

        /// <summary>
        /// Gets the enum by "increment" many places from the CurrentEnum. This
        /// is modular, so if CurrentEnum is the first enum, then this will return 
        /// the last enum, and if CurrentEnum  is the last enum, then this 
        /// will return the first enum.
        /// </summary>
        /// <param name="setToCurrent">If True, sets CurrentEnum = IncrementedEnum</param>
        /// <param name="increment">The amount to increment the CurrentEnum by</param>
        /// <returns>The incremented enum</returns>
        public T IncrementedEnum(bool setToCurrent, int increment)
        {
            var incrementedIndex =
                ((_CurrentEnumIndex + increment % Enums.Length) + Enums.Length)
                % Enums.Length;

            var incrementedEnum = Enums[incrementedIndex];

            if (setToCurrent)
                SetCurrentEnum(incrementedEnum, incrementedIndex);

            return incrementedEnum;
        }

        /// <summary>
        /// Sets the CurrentEnum equal to newEnum. We have a separate method
        /// for the setter since we are keeping track of the index of CurrentEnum
        /// so we do not have to do a linear search everytime we want to find its index.
        /// </summary>
        /// <param name="newEnum">The enum to set as CurrentEnum</param>
        public void SetCurrentEnum(T newEnum)
        {
            var newEnumIndex = Array.IndexOf(Enums, newEnum);
            SetCurrentEnum(newEnum, newEnumIndex);
        }

        /// <summary>
        /// Private version of SetCurrentEnum where just set the private 
        /// field _CurrentEnumIndex as required. 
        /// </summary>
        /// <param name="newEnum">The enum to set as CurrentEnum</param>
        /// <param name="newEnumIndex">The index of the newEnum</param>
        private void SetCurrentEnum(T newEnum, int newEnumIndex)
        {
            CurrentEnum = newEnum;
            _CurrentEnumIndex = newEnumIndex;
        }
    }
} 

As an example of how to use this code, the output of the wrapper's methods are listed as follows:

var playStates = new EnumWrapper<PlayState>();

playStates.CurrentEnum = PlayState.Play

playStates.NextEnum(false) = PlayState.Stop

playStates.PrevEnum(true) = PlayState.HUD

playStates.CurrentEnum = PlayState.HUD

playStates.SetCurrentEnum(PlayState.Play)

CurrentEnum = PlayState.Play

playStates.IncrementedEnum(false, 3003) = PlayState.Options

playStates.IncrementedEnum(false, -3001) = PlayState.HUD 

The EnumWrapper class is useful when you want to set the CurrentEnum given the result of an "action". For example, if you want to turn on and off the functionality of a component dependant on the value of CurrentEnum, then you can do something like:

PlayCommand = new RelayCommand(() =>
{
    PlayStates.NextEnum(true);
}); 

License

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

Share

About the Author

LShep

United Kingdom United Kingdom
No Biography provided

Comments and Discussions

 
QuestionRestricting your class to (almost) enums only Pin
John B Oliver28-Apr-14 13:19
memberJohn B Oliver28-Apr-14 13:19 
QuestionNice Idea Pin
JJVH8410-Apr-14 7:27
memberJJVH8410-Apr-14 7:27 
SuggestionOne suggestion Pin
Tridip Bhattacharjee7-Apr-14 5:41
memberTridip Bhattacharjee7-Apr-14 5:41 
GeneralRe: One suggestion Pin
LShep7-Apr-14 13:40
memberLShep7-Apr-14 13:40 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150520.1 | Last Updated 7 Apr 2014
Article Copyright 2014 by LShep
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid