Turn an Enum into a Simple State Machine
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 enum
s more convenient, especially when representing the state of a system.
Using the Code
Given an arbitraryenum
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);
});