From facc0834f0cfb76a7d7c1ee0ca74c80244baded2 Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Wed, 20 Apr 2022 18:16:59 +0100 Subject: [PATCH] Separate unwrapped Enum.Next/Previous to Unchecked overloads --- X10D.Tests/src/Core/EnumTests.cs | 61 ++++++++++++++++ X10D/X10D.csproj.DotSettings | 1 + X10D/src/EnumExtensions/EnumExtensions.cs | 86 ++++++++++++++++------- 3 files changed, 122 insertions(+), 26 deletions(-) create mode 100644 X10D.Tests/src/Core/EnumTests.cs diff --git a/X10D.Tests/src/Core/EnumTests.cs b/X10D.Tests/src/Core/EnumTests.cs new file mode 100644 index 0000000..8347470 --- /dev/null +++ b/X10D.Tests/src/Core/EnumTests.cs @@ -0,0 +1,61 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace X10D.Tests.Core; + +[TestClass] +public class EnumTests +{ + // Microsoft wrongfully decided to have Sunday be 0, Monday be 1, etc. + // I personally hate this, Sunday is not the first day of the week. + // it's clearly Monday as defined by ISO 8601. + // but Microsoft can't fix this without breaking compatibility. + // I have feelings... + + [TestMethod] + public void Next() + { + Assert.AreEqual(DayOfWeek.Monday, DayOfWeek.Sunday.Next()); + Assert.AreEqual(DayOfWeek.Tuesday, DayOfWeek.Monday.Next()); + Assert.AreEqual(DayOfWeek.Wednesday, DayOfWeek.Tuesday.Next()); + Assert.AreEqual(DayOfWeek.Thursday, DayOfWeek.Wednesday.Next()); + Assert.AreEqual(DayOfWeek.Friday, DayOfWeek.Thursday.Next()); + Assert.AreEqual(DayOfWeek.Saturday, DayOfWeek.Friday.Next()); + Assert.AreEqual(DayOfWeek.Sunday, DayOfWeek.Saturday.Next()); // Saturday is the "last" day. wrap to "first" + } + + [TestMethod] + public void NextUnchecked() + { + Assert.AreEqual(DayOfWeek.Monday, DayOfWeek.Sunday.NextUnchecked()); + Assert.AreEqual(DayOfWeek.Tuesday, DayOfWeek.Monday.NextUnchecked()); + Assert.AreEqual(DayOfWeek.Wednesday, DayOfWeek.Tuesday.NextUnchecked()); + Assert.AreEqual(DayOfWeek.Thursday, DayOfWeek.Wednesday.NextUnchecked()); + Assert.AreEqual(DayOfWeek.Friday, DayOfWeek.Thursday.NextUnchecked()); + Assert.AreEqual(DayOfWeek.Saturday, DayOfWeek.Friday.NextUnchecked()); + Assert.ThrowsException(() => DayOfWeek.Saturday.NextUnchecked()); + } + + [TestMethod] + public void Previous() + { + Assert.AreEqual(DayOfWeek.Saturday, DayOfWeek.Sunday.Previous()); // Sunday is the "first" day. wrap to "last" + Assert.AreEqual(DayOfWeek.Sunday, DayOfWeek.Monday.Previous()); + Assert.AreEqual(DayOfWeek.Monday, DayOfWeek.Tuesday.Previous()); + Assert.AreEqual(DayOfWeek.Tuesday, DayOfWeek.Wednesday.Previous()); + Assert.AreEqual(DayOfWeek.Wednesday, DayOfWeek.Thursday.Previous()); + Assert.AreEqual(DayOfWeek.Thursday, DayOfWeek.Friday.Previous()); + Assert.AreEqual(DayOfWeek.Friday, DayOfWeek.Saturday.Previous()); + } + + [TestMethod] + public void PreviousUnchecked() + { + Assert.AreEqual(DayOfWeek.Sunday, DayOfWeek.Monday.PreviousUnchecked()); + Assert.AreEqual(DayOfWeek.Monday, DayOfWeek.Tuesday.PreviousUnchecked()); + Assert.AreEqual(DayOfWeek.Tuesday, DayOfWeek.Wednesday.PreviousUnchecked()); + Assert.AreEqual(DayOfWeek.Wednesday, DayOfWeek.Thursday.PreviousUnchecked()); + Assert.AreEqual(DayOfWeek.Thursday, DayOfWeek.Friday.PreviousUnchecked()); + Assert.AreEqual(DayOfWeek.Friday, DayOfWeek.Saturday.PreviousUnchecked()); + Assert.ThrowsException(() => DayOfWeek.Sunday.PreviousUnchecked()); + } +} diff --git a/X10D/X10D.csproj.DotSettings b/X10D/X10D.csproj.DotSettings index e6f0c7d..02fbe0a 100644 --- a/X10D/X10D.csproj.DotSettings +++ b/X10D/X10D.csproj.DotSettings @@ -9,6 +9,7 @@ True True True + True True True True diff --git a/X10D/src/EnumExtensions/EnumExtensions.cs b/X10D/src/EnumExtensions/EnumExtensions.cs index 02b80fe..3c84075 100644 --- a/X10D/src/EnumExtensions/EnumExtensions.cs +++ b/X10D/src/EnumExtensions/EnumExtensions.cs @@ -6,44 +6,78 @@ public static class EnumExtensions { /// - /// Returns the next member defined in a specified enum. + /// Returns the value which is defined proceeding this value in the enumeration. /// - /// The enum type. - /// The enum value which should be used as the starting point. - /// - /// Optional. if the final value of should wrap around to the first - /// value; otherwise, . Defaults to . - /// + /// The type of the enumeration. + /// The value whose proceeding value to retrieve. /// - /// A value of that is considered to be the next value defined after - /// . + /// A value of that is considered to be the next value defined after , + /// or the first value if is the final field of the enumeration. /// - public static T Next(this T source, bool wrap = true) + public static T Next(this T value) where T : struct, Enum { - var array = Enum.GetValues(); - int index = Array.IndexOf(array, source) + 1; - return array.Length == index ? array[wrap ? 0 : index - 1] : array[index]; + T[] values = Enum.GetValues(); + int index = Array.IndexOf(values, value) + 1; + index %= values.Length; + return values[index]; } /// - /// Returns the previous member defined in a specified enum. + /// Returns the value which is defined proceeding this value in the enumeration. /// - /// The enum type. - /// The enum value which should be used as the starting point. - /// - /// Optional. if the first value of should wrap around to the final - /// value; otherwise, . Defaults to . - /// + /// The type of the enumeration. + /// The value whose proceeding value to retrieve. /// - /// A value of that is considered to be the previous value defined before - /// . + /// A value of that is considered to be the next value defined after + /// . /// - public static T Previous(this T source, bool wrap = true) + /// is the final field of the enumeration. + public static T NextUnchecked(this T value) where T : struct, Enum { - var array = Enum.GetValues(); - int index = Array.IndexOf(array, source) - 1; - return index < 0 ? array[wrap ? array.Length - 1 : 0] : array[index]; + T[] values = Enum.GetValues(); + int index = Array.IndexOf(values, value) + 1; + return values[index]; + } + + /// + /// Returns the value which is defined preceeding this value in the enumeration. + /// + /// The type of the enumeration. + /// The value whose preceeding value to retrieve. + /// + /// A value of that is considered to be the previous value defined after + /// , or the last value if is the first field of the enumeration. + /// + public static T Previous(this T value) + where T : struct, Enum + { + T[] values = Enum.GetValues(); + int index = Array.IndexOf(values, value) - 1; + int length = values.Length; + + // negative modulo is not supported in C#. workaround: https://stackoverflow.com/a/1082938/1467293 + // sure, simply checking for index < 0 is enough, but this expression is so fucking cool! + index = (index % length + length) % length; + return values[index]; + } + + /// + /// Returns the value which is defined preceeding this value in the enumeration. + /// + /// The type of the enumeration. + /// The value whose preceeding value to retrieve. + /// + /// A value of that is considered to be the previous value defined after + /// , or the last value if is the first field of the enumeration. + /// + /// is the first field of the enumeration. + public static T PreviousUnchecked(this T value) + where T : struct, Enum + { + T[] values = Enum.GetValues(); + int index = Array.IndexOf(values, value) - 1; + return values[index]; } }