Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Turn an Enum into a Simple State Machine

0.00/5 (No votes)
6 Apr 2014 1  
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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here