Last active
November 9, 2019 13:33
-
-
Save xepherys/4f79bd63c23332a83ed1e4d8b22c532a to your computer and use it in GitHub Desktop.
Rotating values of a flagged System.Enum in C#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Reflection; | |
// Requires C# 7.3 or later (Roslyn 3.0 or later) for where TEnum : System.Enum | |
public class Program | |
{ | |
public static void Main() | |
{ | |
CompassDirections dirs = CompassDirections.NORTH | CompassDirections.SOUTHEAST; | |
#region Test functionality | |
Console.WriteLine("Valid directions are: {0}\n", dirs); | |
// Rotation as a standard method | |
dirs = Rotate(dirs, 2); | |
Console.WriteLine("Updated directions are: {0}\n", dirs); | |
// Rotation as an extension method to TEnum | |
dirs.Rotate(6); | |
Console.WriteLine("Updated directions are: {0}\n", dirs); | |
#endregion | |
} | |
// Standard method | |
public static TEnum Rotate<TEnum>(TEnum inEnum, int positions = 1) where TEnum : System.Enum | |
{ | |
if (positions == 0) | |
{ | |
Console.WriteLine("Rotate<TEnum>() called to rotate 0 positions. No rotation will occur."); | |
return inEnum; | |
} | |
// Currently doesn't support any underlying enum type but int | |
if (Enum.GetUnderlyingType(inEnum.GetType()) != typeof(int)) | |
throw new NotSupportedException("This method, Rotate<TEnum>(), is only valud on integer underlying types. The System,Enum '" + inEnum.GetType().Name + "' does not use such an underlying value."); | |
if (!typeof(TEnum).GetCustomAttributes<FlagsAttribute>().Any()) | |
throw new NotSupportedException("This method, Rotate<TEnum>(), is only valid for enums that have the [System.Flags] attribute set. The System.Enum '" + inEnum.GetType().Name + "' does not use that attribute."); | |
List<int> values = new List<int>((int[])Enum.GetValues(inEnum.GetType())); | |
if (values.Any(a => a < 0)) | |
throw new NotSupportedException("This method, Rotate<TEnum>(), is only valid for enums that contain no negative integer values. The System.Enum '" + inEnum.GetType().Name + "' includes negative values."); | |
if (((int)(object)inEnum ^ 0) == 0) | |
{ | |
Console.WriteLine("Rotate<TEnum>() called to rotate an enum that is set to 'NONE = 0'. No rotation will occur."); | |
return inEnum; | |
} | |
int allValuesMask = values.Max(); | |
if (((int)(object)inEnum ^ allValuesMask) == 0) | |
{ | |
Console.WriteLine("Rotate<TEnum>() called to rotate an enum that is set to 'ALL', 'OMNI', or a similar status including all flags. No rotation will occur."); | |
return inEnum; | |
} | |
// Remove 0/NONE and OMNI/ALL | |
values.Remove(0); | |
values.Remove(values.Max()); | |
if (positions % values.Count == 0) | |
{ | |
Console.WriteLine("Rotate<ENum>() called with perfect roundness positions % possiblePositions == 0. No rotation will occur."); | |
return inEnum; | |
} | |
int maxRotation = Convert.ToString(values.Max(), 2).Length; | |
while (Math.Abs(positions) > maxRotation) | |
{ | |
if (positions > 0) | |
positions -= maxRotation; | |
else | |
positions += maxRotation; | |
} | |
if (positions > 0) | |
{ | |
return (TEnum)(object)(((int)(object)inEnum << positions | (int)(object)inEnum >> (maxRotation - positions)) & allValuesMask); | |
} | |
else if (positions < 0) | |
{ | |
positions = Math.Abs(positions); | |
return (TEnum)(object)(((int)(object)inEnum >> positions | (int)(object)inEnum << (maxRotation - positions)) & allValuesMask); | |
} | |
Console.WriteLine("Rotate<TEnum>() Unable to rotate - unknown condition."); | |
return inEnum; | |
} | |
} | |
public static class EnumExtensions | |
{ | |
// Extension of TEnum | |
public static void Rotate<TEnum>(this ref TEnum inEnum, int positions = 1) where TEnum : struct, System.Enum | |
{ | |
if (positions == 0) | |
{ | |
Console.WriteLine("Rotate<TEnum>() called to rotate 0 positions. No rotation will occur."); | |
return; | |
} | |
// Currently doesn't support any underlying enum type but int | |
if (Enum.GetUnderlyingType(inEnum.GetType()) != typeof(int)) | |
throw new NotSupportedException("This method, Rotate<TEnum>(), is only valud on integer underlying types. The System,Enum '" + inEnum.GetType().Name + "' does not use such an underlying value."); | |
if (!typeof(TEnum).GetCustomAttributes<FlagsAttribute>().Any()) | |
throw new NotSupportedException("This method, Rotate<TEnum>(), is only valid for enums that have the [System.Flags] attribute set. The System.Enum '" + inEnum.GetType().Name + "' does not use that attribute."); | |
List<int> values = new List<int>((int[])Enum.GetValues(inEnum.GetType())); | |
if (values.Any(a => a < 0)) | |
throw new NotSupportedException("This method, Rotate<TEnum>(), is only valid for enums that contain no negative integer values. The System.Enum '" + inEnum.GetType().Name + "' includes negative values."); | |
if (((int)(object)inEnum ^ 0) == 0) | |
{ | |
Console.WriteLine("Rotate<TEnum>() called to rotate an enum that is set to 'NONE = 0'. No rotation will occur."); | |
return; | |
} | |
int allValuesMask = values.Max(); | |
if (((int)(object)inEnum ^ allValuesMask) == 0) | |
{ | |
Console.WriteLine("Rotate<TEnum>() called to rotate an enum that is set to 'ALL', 'OMNI', or a similar status including all flags. No rotation will occur."); | |
return; | |
} | |
// Remove 0/NONE and OMNI/ALL | |
values.Remove(0); | |
values.Remove(values.Max()); | |
if (positions % values.Count == 0) | |
{ | |
Console.WriteLine("Rotate<ENum>() called with perfect roundness positions % possiblePositions == 0. No rotation will occur."); | |
return; | |
} | |
int maxRotation = Convert.ToString(values.Max(), 2).Length; | |
while (Math.Abs(positions) > maxRotation) | |
{ | |
if (positions > 0) | |
positions -= maxRotation; | |
else | |
positions += maxRotation; | |
} | |
if (positions > 0) | |
{ | |
inEnum = (TEnum)(object)(((int)(object)inEnum << positions | (int)(object)inEnum >> (maxRotation - positions)) & allValuesMask); | |
return; | |
} | |
else if (positions < 0) | |
{ | |
positions = Math.Abs(positions); | |
inEnum = (TEnum)(object)(((int)(object)inEnum >> positions | (int)(object)inEnum << (maxRotation - positions)) & allValuesMask); | |
return; | |
} | |
Console.WriteLine("Rotate<TEnum>() Unable to rotate - unknown condition."); | |
return; | |
} | |
} | |
[System.Flags] | |
public enum CompassDirections | |
{ | |
NONE = 0, | |
NORTH = 1 << 0, | |
NORTHEAST = 1 << 1, | |
EAST = 1 << 2, | |
SOUTHEAST = 1 << 3, | |
SOUTH = 1 << 4, | |
SOUTHWEST = 1 << 5, | |
WEST = 1 << 6, | |
NORTHWEST = 1 << 7, | |
OMNI = ~(~0 << 8) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment