From 3129d3e60c8b8412e972bddfbba1e6376ec5b038 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Wed, 25 Jul 2018 15:53:39 -0400 Subject: [PATCH] perf: Further reducing enum boxing --- ICD.Common.Utils.Tests/EnumUtilsTest.cs | 38 +------ ICD.Common.Utils/EnumUtils.cs | 145 +++++++----------------- 2 files changed, 42 insertions(+), 141 deletions(-) diff --git a/ICD.Common.Utils.Tests/EnumUtilsTest.cs b/ICD.Common.Utils.Tests/EnumUtilsTest.cs index 15a965e..c36a4db 100644 --- a/ICD.Common.Utils.Tests/EnumUtilsTest.cs +++ b/ICD.Common.Utils.Tests/EnumUtilsTest.cs @@ -37,16 +37,6 @@ namespace ICD.Common.Utils.Tests Assert.AreEqual(eTestEnum.C, values[3]); } - [Test] - public void IsEnumTypeTest() - { - Assert.IsTrue(EnumUtils.IsEnumType(typeof(eTestEnum))); - Assert.IsTrue(EnumUtils.IsEnumType(typeof(eTestFlagsEnum))); - Assert.IsTrue(EnumUtils.IsEnumType(typeof(Enum))); - Assert.IsFalse(EnumUtils.IsEnumType(typeof(EnumUtilsTest))); - Assert.Throws(() => EnumUtils.IsEnumType(null)); - } - [Test] public void IsEnumTypeGenericTest() { @@ -63,7 +53,7 @@ namespace ICD.Common.Utils.Tests Assert.IsTrue(EnumUtils.IsEnum(eTestFlagsEnum.A)); Assert.IsTrue(EnumUtils.IsEnum(eTestEnum.A as object)); Assert.IsTrue(EnumUtils.IsEnum(eTestEnum.A as Enum)); - Assert.IsFalse(EnumUtils.IsEnum(null)); + Assert.IsFalse(EnumUtils.IsEnum(null)); Assert.IsFalse(EnumUtils.IsEnum("")); } @@ -91,13 +81,6 @@ namespace ICD.Common.Utils.Tests Assert.IsTrue(values.Contains(eTestEnum.C)); } - [Test] - public void GetNoneValueGenericTest() - { - Assert.AreEqual(eTestEnum.None, EnumUtils.GetNoneValue()); - Assert.AreEqual(eTestFlagsEnum.None, EnumUtils.GetNoneValue()); - } - [Test] public void GetValuesExceptNoneGenericTest() { @@ -110,18 +93,6 @@ namespace ICD.Common.Utils.Tests Assert.IsTrue(values.Contains(eTestEnum.C)); } - [Test] - public void GetValuesExceptNoneTest() - { - eTestEnum[] values = EnumUtils.GetValuesExceptNone(typeof(eTestEnum)).Cast().ToArray(); - - Assert.AreEqual(3, values.Length); - Assert.IsFalse(values.Contains(eTestEnum.None)); - Assert.IsTrue(values.Contains(eTestEnum.A)); - Assert.IsTrue(values.Contains(eTestEnum.B)); - Assert.IsTrue(values.Contains(eTestEnum.C)); - } - #endregion #region Flags @@ -133,13 +104,6 @@ namespace ICD.Common.Utils.Tests Assert.IsTrue(EnumUtils.IsFlagsEnum()); } - [Test] - public void IsFlagsEnumTest() - { - Assert.IsFalse(EnumUtils.IsFlagsEnum(typeof(eTestEnum))); - Assert.IsTrue(EnumUtils.IsFlagsEnum(typeof(eTestFlagsEnum))); - } - [Test] public void GetFlagsIntersectionGenericTest() { diff --git a/ICD.Common.Utils/EnumUtils.cs b/ICD.Common.Utils/EnumUtils.cs index 542412f..461d7c7 100644 --- a/ICD.Common.Utils/EnumUtils.cs +++ b/ICD.Common.Utils/EnumUtils.cs @@ -13,33 +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>(); - } - - /// - /// Returns true if the given type is an enum. - /// - /// - public static bool IsEnumType(Type type) - { - if (type == null) - throw new ArgumentNullException("type"); - - // Type - return type -#if !SIMPLSHARP - .GetTypeInfo() -#endif - .IsEnum || type.IsAssignableTo(typeof(Enum)); + s_EnumValuesCache = new Dictionary(); + s_EnumFlagsCache = new Dictionary>(); } /// @@ -51,6 +34,22 @@ namespace ICD.Common.Utils return IsEnumType(typeof(T)); } + /// + /// Returns true if the given type is an enum. + /// + /// + private static bool IsEnumType(Type type) + { + if (type == null) + throw new ArgumentNullException("type"); + + return type +#if !SIMPLSHARP + .GetTypeInfo() +#endif + .IsEnum || type.IsAssignableTo(typeof(Enum)); + } + /// /// Returns true if the given value is an enum. /// @@ -58,7 +57,7 @@ namespace ICD.Common.Utils public static bool IsEnum(T value) { // ReSharper disable once CompareNonConstrainedGenericWithNull - return value != null && IsEnumType(); + return value != null && IsEnumType(value.GetType()); } /// @@ -69,7 +68,7 @@ namespace ICD.Common.Utils /// public static bool IsDefined(T value) { - if (!IsEnumType(typeof(T))) + if (!IsEnumType()) throw new InvalidOperationException(string.Format("{0} is not an enum", typeof(T).Name)); if (!IsFlagsEnum()) @@ -96,41 +95,28 @@ namespace ICD.Common.Utils /// public static IEnumerable GetValues() { - return GetValues(typeof(T)).Cast(); - } - - /// - /// Gets the values from an enumeration. - /// - /// - /// - public static IEnumerable GetValues(Type type) - { - if (type == null) - throw new ArgumentNullException("type"); + Type type = typeof(T); // Reflection is slow and this method is called a lot, so we cache the results. - int[] cache; + object cache; if (!s_EnumValuesCache.TryGetValue(type, out cache)) { - cache = GetValuesUncached(type).Cast().ToArray(); + cache = GetValuesUncached().ToArray(); s_EnumValuesCache[type] = cache; } - return cache; + return cache as T[]; } /// /// 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() { - if (type == null) - throw new ArgumentNullException("type"); + Type type = typeof(T); - if (!IsEnumType(type)) + if (!IsEnumType()) throw new InvalidOperationException(string.Format("{0} is not an enum", type.Name)); return type @@ -140,20 +126,8 @@ namespace ICD.Common.Utils .GetTypeInfo() #endif .GetFields(BindingFlags.Static | BindingFlags.Public) - .Select(x => x.GetValue(null)); - } - - /// - /// Gets the 0 value for the given enum type. - /// - /// - /// - public static T GetNoneValue() - { - if (!IsEnumType(typeof(T))) - throw new InvalidOperationException(string.Format("{0} is not an enum", typeof(T).Name)); - - return (T)(object)0; + .Select(x => x.GetValue(null)) + .Cast(); } /// @@ -163,26 +137,7 @@ namespace ICD.Common.Utils /// public static IEnumerable GetValuesExceptNone() { - if (!IsEnumType(typeof(T))) - throw new InvalidOperationException(string.Format("{0} is not an enum", typeof(T).Name)); - - return GetValuesExceptNone(typeof(T)).Cast(); - } - - /// - /// Gets the values from an enumeration except the 0 value. - /// - /// - /// - public static IEnumerable GetValuesExceptNone(Type type) - { - if (type == null) - throw new ArgumentNullException("type"); - - if (!IsEnumType(type)) - throw new InvalidOperationException(string.Format("{0} is not an enum", type.Name)); - - return GetValues(type).Where(v => v != 0); + return GetFlagsExceptNone(); } #endregion @@ -199,22 +154,7 @@ namespace ICD.Common.Utils if (!IsEnumType()) throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name)); - return IsFlagsEnum(typeof(T)); - } - - /// - /// Returns true if the given enum type has the Flags attribute set. - /// - /// - public static bool IsFlagsEnum(Type type) - { - if (type == null) - throw new ArgumentNullException("type"); - - if (!IsEnumType(type)) - throw new InvalidOperationException(string.Format("{0} is not an enum", type.Name)); - - return type + return typeof(T) #if !SIMPLSHARP .GetTypeInfo() #endif @@ -230,7 +170,7 @@ namespace ICD.Common.Utils public static T GetFlagsIntersection(params T[] values) { if (values.Length == 0) - return GetNoneValue(); + return default(T); int output = (int)(object)values.First(); foreach (T item in values.Skip(1)) @@ -269,23 +209,21 @@ namespace ICD.Common.Utils Type type = typeof(T); int valueInt = (int)(object)value; - Dictionary cache; + Dictionary cache; if (!s_EnumFlagsCache.TryGetValue(type, out cache)) { - cache = new Dictionary(); + cache = new Dictionary(); s_EnumFlagsCache[type] = cache; } - int[] flags; + object flags; if (!cache.TryGetValue(valueInt, out flags)) { - flags = GetValues().Where(e => HasFlag(value, e)) - .Cast() - .ToArray(); + flags = GetValues().Where(e => HasFlag(value, e)).ToArray(); cache[valueInt] = flags; } - return flags.Cast(); + return flags as T[]; } /// @@ -314,8 +252,7 @@ namespace ICD.Common.Utils // ReSharper disable once CompareNonConstrainedGenericWithNull throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value"); - T none = GetNoneValue(); - return GetFlags(value).Except(none); + return GetFlags(value).Except(default(T)); } /// @@ -333,7 +270,7 @@ namespace ICD.Common.Utils // ReSharper disable once CompareNonConstrainedGenericWithNull throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value"); - int maxEnumValue = (GetValues().Max(v => (int)(object)v) * 2) -1 ; + int maxEnumValue = (GetValues().Max(v => (int)(object)v) * 2) -1; return Enumerable.Range(1, maxEnumValue).Select(i => (T)(object)i ).Where(v => HasFlags(value, v)); }