diff --git a/ICD.Common.Utils.Tests/EnumUtilsTest.cs b/ICD.Common.Utils.Tests/EnumUtilsTest.cs index 48820b4..6466262 100644 --- a/ICD.Common.Utils.Tests/EnumUtilsTest.cs +++ b/ICD.Common.Utils.Tests/EnumUtilsTest.cs @@ -5,12 +5,12 @@ using System.Linq; namespace ICD.Common.Utils.Tests { [TestFixture] - public sealed class EnumUtilsTest - { + public sealed class EnumUtilsTest + { public enum eTestEnum { None = 0, - A = 1, + A = 1, B = 2, C = 3, } @@ -24,7 +24,7 @@ namespace ICD.Common.Utils.Tests C = 4, D = 32 } - + [Test] public void GetValuesTest() { @@ -36,7 +36,7 @@ namespace ICD.Common.Utils.Tests Assert.AreEqual(eTestEnum.B, values[2]); Assert.AreEqual(eTestEnum.C, values[3]); } - + [Test] public void IsEnumTypeGenericTest() { @@ -57,17 +57,29 @@ namespace ICD.Common.Utils.Tests Assert.IsFalse(EnumUtils.IsEnum("")); } - [Test] - public void IsDefinedTest() - { - Assert.IsFalse(EnumUtils.IsDefined((eTestEnum)20)); - Assert.IsTrue(EnumUtils.IsDefined((eTestEnum)2)); + [Test] + public void IsDefinedTest() + { + Assert.IsFalse(EnumUtils.IsDefined((eTestEnum)20)); + Assert.IsTrue(EnumUtils.IsDefined((eTestEnum)2)); - Assert.IsFalse(EnumUtils.IsDefined((eTestFlagsEnum)8)); - Assert.IsTrue(EnumUtils.IsDefined((eTestFlagsEnum)3)); - } + Assert.IsFalse(EnumUtils.IsDefined((eTestFlagsEnum)8)); + Assert.IsTrue(EnumUtils.IsDefined((eTestFlagsEnum)3)); + } - #region Values + #region Values + + [Test] + public void GetNamesTest() + { + string[] names = EnumUtils.GetNames().ToArray(); + + Assert.AreEqual(4, names.Length); + Assert.IsTrue(names.Contains("None")); + Assert.IsTrue(names.Contains("A")); + Assert.IsTrue(names.Contains("B")); + Assert.IsTrue(names.Contains("C")); + } [Test] public void GetValuesGenericTest() @@ -107,7 +119,9 @@ namespace ICD.Common.Utils.Tests [Test] public void GetFlagsIntersectionGenericTest() { - Assert.AreEqual(eTestFlagsEnum.B, EnumUtils.GetFlagsIntersection(eTestFlagsEnum.A | eTestFlagsEnum.B, eTestFlagsEnum.B | eTestFlagsEnum.C)); + Assert.AreEqual(eTestFlagsEnum.B, + EnumUtils.GetFlagsIntersection(eTestFlagsEnum.A | eTestFlagsEnum.B, + eTestFlagsEnum.B | eTestFlagsEnum.C)); } [Test] @@ -138,11 +152,11 @@ namespace ICD.Common.Utils.Tests Assert.IsTrue(aValues.Contains(eTestFlagsEnum.D)); } - [Test] - public void GetAllFlagCombinationsExceptNoneGenericTest() - { - eTestFlagsEnum a = eTestFlagsEnum.A | eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D; - eTestFlagsEnum[] aValues = EnumUtils.GetAllFlagCombinationsExceptNone(a).ToArray(); + [Test] + public void GetAllFlagCombinationsExceptNoneGenericTest() + { + eTestFlagsEnum a = eTestFlagsEnum.A | eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D; + eTestFlagsEnum[] aValues = EnumUtils.GetAllFlagCombinationsExceptNone(a).ToArray(); Assert.AreEqual(15, aValues.Length); Assert.IsFalse(aValues.Contains(eTestFlagsEnum.None)); @@ -167,7 +181,8 @@ namespace ICD.Common.Utils.Tests public void GetFlagsAllValueGenericTest() { eTestFlagsEnum value = EnumUtils.GetFlagsAllValue(); - Assert.AreEqual(eTestFlagsEnum.None | eTestFlagsEnum.A | eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D, value); + Assert.AreEqual(eTestFlagsEnum.None | eTestFlagsEnum.A | eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D, + value); } [Test] @@ -181,7 +196,8 @@ namespace ICD.Common.Utils.Tests public void HasFlagsGenericTest() { Assert.IsTrue(EnumUtils.HasFlags(eTestFlagsEnum.A | eTestFlagsEnum.B, eTestFlagsEnum.A | eTestFlagsEnum.B)); - Assert.IsFalse(EnumUtils.HasFlags(eTestFlagsEnum.A | eTestFlagsEnum.B, eTestFlagsEnum.A | eTestFlagsEnum.C)); + Assert.IsFalse(EnumUtils.HasFlags(eTestFlagsEnum.A | eTestFlagsEnum.B, + eTestFlagsEnum.A | eTestFlagsEnum.C)); } [Test] @@ -200,28 +216,28 @@ namespace ICD.Common.Utils.Tests Assert.IsTrue(EnumUtils.HasMultipleFlags(eTestFlagsEnum.A | eTestFlagsEnum.B)); } - [TestCase(false, eTestFlagsEnum.None)] - [TestCase(true, eTestFlagsEnum.B)] - [TestCase(true, eTestFlagsEnum.B | eTestFlagsEnum.C)] - public void HasAnyFlagsTest(bool expected, eTestFlagsEnum value) - { - Assert.AreEqual(expected, EnumUtils.HasAnyFlags(value)); - } + [TestCase(false, eTestFlagsEnum.None)] + [TestCase(true, eTestFlagsEnum.B)] + [TestCase(true, eTestFlagsEnum.B | eTestFlagsEnum.C)] + public void HasAnyFlagsTest(bool expected, eTestFlagsEnum value) + { + Assert.AreEqual(expected, EnumUtils.HasAnyFlags(value)); + } - [TestCase(false, eTestFlagsEnum.None, eTestFlagsEnum.None)] - [TestCase(false, eTestFlagsEnum.None, eTestFlagsEnum.B)] - [TestCase(true, eTestFlagsEnum.B, eTestFlagsEnum.B)] - [TestCase(false, eTestFlagsEnum.None | eTestFlagsEnum.A, eTestFlagsEnum.B | eTestFlagsEnum.C)] - public void HasAnyFlagsValueTest(bool expected, eTestFlagsEnum value, eTestFlagsEnum other) - { - Assert.AreEqual(expected, EnumUtils.HasAnyFlags(value, other)); - } + [TestCase(false, eTestFlagsEnum.None, eTestFlagsEnum.None)] + [TestCase(false, eTestFlagsEnum.None, eTestFlagsEnum.B)] + [TestCase(true, eTestFlagsEnum.B, eTestFlagsEnum.B)] + [TestCase(false, eTestFlagsEnum.None | eTestFlagsEnum.A, eTestFlagsEnum.B | eTestFlagsEnum.C)] + public void HasAnyFlagsValueTest(bool expected, eTestFlagsEnum value, eTestFlagsEnum other) + { + Assert.AreEqual(expected, EnumUtils.HasAnyFlags(value, other)); + } - #endregion + #endregion - #region Conversion + #region Conversion - [Test] + [Test] public void ParseGenericTest() { Assert.AreEqual(eTestEnum.A, EnumUtils.Parse("A", false)); @@ -243,35 +259,35 @@ namespace ICD.Common.Utils.Tests Assert.AreEqual(default(eTestEnum), output); } - [Test] - public void ParseStrictGenericTest() - { - Assert.AreEqual(eTestEnum.A, EnumUtils.ParseStrict("1", false)); - Assert.Throws(() => EnumUtils.ParseStrict("4", false)); + [Test] + public void ParseStrictGenericTest() + { + Assert.AreEqual(eTestEnum.A, EnumUtils.ParseStrict("1", false)); + Assert.Throws(() => EnumUtils.ParseStrict("4", false)); - Assert.AreEqual(eTestFlagsEnum.A | eTestFlagsEnum.B, EnumUtils.ParseStrict("3", false)); - Assert.Throws(() => EnumUtils.ParseStrict("8", false)); - } + Assert.AreEqual(eTestFlagsEnum.A | eTestFlagsEnum.B, EnumUtils.ParseStrict("3", false)); + Assert.Throws(() => EnumUtils.ParseStrict("8", false)); + } - [Test] - public void TryParseStrictGenericTest() - { - eTestEnum outputA; + [Test] + public void TryParseStrictGenericTest() + { + eTestEnum outputA; - Assert.AreEqual(true, EnumUtils.TryParseStrict("1", false, out outputA)); - Assert.AreEqual(eTestEnum.A, outputA); + Assert.AreEqual(true, EnumUtils.TryParseStrict("1", false, out outputA)); + Assert.AreEqual(eTestEnum.A, outputA); - Assert.AreEqual(false, EnumUtils.TryParseStrict("4", false, out outputA)); - Assert.AreEqual(eTestEnum.None, outputA); + Assert.AreEqual(false, EnumUtils.TryParseStrict("4", false, out outputA)); + Assert.AreEqual(eTestEnum.None, outputA); - eTestFlagsEnum outputB; + eTestFlagsEnum outputB; - Assert.AreEqual(true, EnumUtils.TryParseStrict("3", false, out outputB)); - Assert.AreEqual(eTestFlagsEnum.A | eTestFlagsEnum.B, outputB); + Assert.AreEqual(true, EnumUtils.TryParseStrict("3", false, out outputB)); + Assert.AreEqual(eTestFlagsEnum.A | eTestFlagsEnum.B, outputB); - Assert.AreEqual(false, EnumUtils.TryParseStrict("8", false, out outputB)); - Assert.AreEqual(eTestFlagsEnum.None, outputB); - } + Assert.AreEqual(false, EnumUtils.TryParseStrict("8", false, out outputB)); + Assert.AreEqual(eTestFlagsEnum.None, outputB); + } #endregion } diff --git a/ICD.Common.Utils/EnumUtils.cs b/ICD.Common.Utils/EnumUtils.cs index 07cdee3..d8289fe 100644 --- a/ICD.Common.Utils/EnumUtils.cs +++ b/ICD.Common.Utils/EnumUtils.cs @@ -13,16 +13,16 @@ namespace ICD.Common.Utils { public static class EnumUtils { - private static readonly Dictionary s_EnumValuesCache; - private static readonly Dictionary> s_EnumFlagsCache; + private static readonly Dictionary s_EnumValuesCache; + private static readonly Dictionary> s_EnumFlagsCache; /// /// Static constructor. /// static EnumUtils() { - s_EnumValuesCache = new Dictionary(); - s_EnumFlagsCache = new Dictionary>(); + s_EnumValuesCache = new Dictionary(); + s_EnumFlagsCache = new Dictionary>(); } #region Validation @@ -58,7 +58,6 @@ namespace ICD.Common.Utils /// public static bool IsEnum(T value) { -// ReSharper disable once CompareNonConstrainedGenericWithNull return value != null && IsEnumType(value.GetType()); } @@ -123,6 +122,30 @@ namespace ICD.Common.Utils #region Values + /// + /// Gets the names of the values in the enumeration. + /// + /// + /// + public static IEnumerable GetNames() + where T : struct, IConvertible + { + return GetNames(typeof(T)); + } + + /// + /// Gets the names of the values in the enumeration. + /// + /// + /// + public static IEnumerable GetNames([NotNull] Type type) + { + if (type == null) + throw new ArgumentNullException("type"); + + return GetValues(type).Select(v => v.ToString()); + } + /// /// Gets the values from an enumeration. /// @@ -131,46 +154,51 @@ namespace ICD.Common.Utils public static IEnumerable GetValues() where T : struct, IConvertible { - Type type = typeof(T); - - // Reflection is slow and this method is called a lot, so we cache the results. - object cache; - if (!s_EnumValuesCache.TryGetValue(type, out cache)) - { - cache = GetValuesUncached().ToArray(); - s_EnumValuesCache[type] = cache; - } - - return cache as T[]; + return GetValues(typeof(T)).Cast(); } /// - /// Gets the values from an enumeration without performing any caching. This is slow because of reflection. + /// Gets the values from an enumeration. /// /// - public static IEnumerable GetValues(Type type) + public static IEnumerable GetValues(Type type) { if (type == null) throw new ArgumentNullException("type"); - return GetValuesUncached(type); + // Reflection is slow and this method is called a lot, so we cache the results. + return s_EnumValuesCache.GetOrAddNew(type, () => GetValuesUncached(type).ToArray()); } /// - /// Gets the values from an enumeration without performing any caching. This is slow because of reflection. + /// Gets the values from an enumeration except the 0 value. /// + /// /// - private static IEnumerable GetValuesUncached() + public static IEnumerable GetValuesExceptNone() where T : struct, IConvertible { - return GetValuesUncached(typeof(T)).Select(i => (T)(object)i); + return GetValuesExceptNone(typeof(T)).Cast(); + } + + /// + /// Gets the values from an enumeration except the 0 value. + /// + /// + /// + public static IEnumerable GetValuesExceptNone([NotNull] Type type) + { + if (type == null) + throw new ArgumentNullException("type"); + + return GetFlagsExceptNone(type); } /// /// Gets the values from an enumeration without performing any caching. This is slow because of reflection. /// /// - private static IEnumerable GetValuesUncached(Type type) + private static IEnumerable GetValuesUncached(Type type) { if (type == null) throw new ArgumentNullException("type"); @@ -182,34 +210,10 @@ namespace ICD.Common.Utils #if SIMPLSHARP .GetCType() #else - .GetTypeInfo() + .GetTypeInfo() #endif - .GetFields(BindingFlags.Static | BindingFlags.Public) - .Select(x => x.GetValue(null)) - .Cast(); - } - - /// - /// Gets the values from an enumeration except the 0 value. - /// - /// - /// - public static IEnumerable GetValuesExceptNone() - where T : struct, IConvertible - { - return GetFlagsExceptNone(); - } - - /// - /// Gets the values from an enumeration except the 0 value without performing any caching. This is slow because of reflection. - /// - /// - public static IEnumerable GetValuesExceptNone(Type type) - { - if (type == null) - throw new ArgumentNullException("type"); - - return GetValues(type).Except(0); + .GetFields(BindingFlags.Static | BindingFlags.Public) + .Select(x => x.GetValue(null)); } #endregion @@ -330,24 +334,21 @@ namespace ICD.Common.Utils public static IEnumerable GetFlags(T value) where T : struct, IConvertible { - Type type = typeof(T); - int valueInt = (int)(object)value; + return GetFlags(typeof(T), value).Cast(); + } - Dictionary cache; - if (!s_EnumFlagsCache.TryGetValue(type, out cache)) - { - cache = new Dictionary(); - s_EnumFlagsCache[type] = cache; - } - - object flags; - if (!cache.TryGetValue(valueInt, out flags)) - { - flags = GetValues().Where(e => HasFlag(value, e)).ToArray(); - cache[valueInt] = flags; - } - - return flags as T[]; + /// + /// Gets all of the set flags on the given enum. + /// + /// + /// + /// + public static IEnumerable GetFlags(Type type, object value) + { + return s_EnumFlagsCache.GetOrAddNew(type, () => new Dictionary()) + .GetOrAddNew(value, () => GetValues(type) + .Where(f => HasFlag(value, f)) + .ToArray()); } /// @@ -358,8 +359,7 @@ namespace ICD.Common.Utils public static IEnumerable GetFlagsExceptNone() where T : struct, IConvertible { - T allValue = GetFlagsAllValue(); - return GetFlagsExceptNone(allValue); + return GetFlagsExceptNone(typeof(T)).Cast(); } /// @@ -371,7 +371,35 @@ namespace ICD.Common.Utils public static IEnumerable GetFlagsExceptNone(T value) where T : struct, IConvertible { - return GetFlags(value).Except(default(T)); + return GetFlagsExceptNone(typeof(T), value).Cast(); + } + + /// + /// Gets all of the set flags on the given enum type except 0. + /// + /// + /// + public static IEnumerable GetFlagsExceptNone([NotNull] Type type) + { + if (type == null) + throw new ArgumentNullException("type"); + + object allValue = GetFlagsAllValue(type); + return GetFlagsExceptNone(type, allValue); + } + + /// + /// Gets all of the set flags on the given enum except 0. + /// + /// + /// + /// + public static IEnumerable GetFlagsExceptNone([NotNull] Type type, object value) + { + if (type == null) + throw new ArgumentNullException("type"); + + return GetFlags(type, value).Where(f => (int)f != 0); } /// @@ -412,7 +440,7 @@ namespace ICD.Common.Utils if (type == null) throw new ArgumentNullException("type"); - return GetValuesUncached(type).Aggregate(0, (current, value) => current | value); + return GetValuesUncached(type).Aggregate(0, (current, value) => current | (int)value); } /// @@ -428,6 +456,17 @@ namespace ICD.Common.Utils return HasFlags(value, flag); } + /// + /// Returns true if the enum contains the given flag. + /// + /// + /// + /// + public static bool HasFlag(object value, object flag) + { + return HasFlag((int)value, (int)flag); + } + /// /// Returns true if the enum contains the given flag. ///