perf: Reducing boxing operations in enum utils

This commit is contained in:
Chris Cameron
2018-07-25 14:23:25 -04:00
parent b8225b7842
commit 591240d973
2 changed files with 19 additions and 44 deletions

View File

@@ -79,15 +79,6 @@ namespace ICD.Common.Utils.Tests
#region Values #region Values
[Test]
public void GetUnderlyingValueTest()
{
Assert.AreEqual(0, EnumUtils.GetUnderlyingValue(eTestEnum.None));
Assert.AreEqual(1, EnumUtils.GetUnderlyingValue(eTestEnum.A));
Assert.AreEqual(2, EnumUtils.GetUnderlyingValue(eTestEnum.B));
Assert.AreEqual(3, EnumUtils.GetUnderlyingValue(eTestEnum.C));
}
[Test] [Test]
public void GetValuesGenericTest() public void GetValuesGenericTest()
{ {
@@ -122,7 +113,7 @@ namespace ICD.Common.Utils.Tests
[Test] [Test]
public void GetValuesExceptNoneTest() public void GetValuesExceptNoneTest()
{ {
object[] values = EnumUtils.GetValuesExceptNone(typeof(eTestEnum)).ToArray(); eTestEnum[] values = EnumUtils.GetValuesExceptNone(typeof(eTestEnum)).Cast<eTestEnum>().ToArray();
Assert.AreEqual(3, values.Length); Assert.AreEqual(3, values.Length);
Assert.IsFalse(values.Contains(eTestEnum.None)); Assert.IsFalse(values.Contains(eTestEnum.None));

View File

@@ -14,16 +14,16 @@ namespace ICD.Common.Utils
{ {
public static class EnumUtils public static class EnumUtils
{ {
private static readonly Dictionary<Type, object[]> s_EnumValuesCache; private static readonly Dictionary<Type, int[]> s_EnumValuesCache;
private static readonly Dictionary<Type, Dictionary<object, object[]>> s_EnumFlagsCache; private static readonly Dictionary<Type, Dictionary<int, int[]>> s_EnumFlagsCache;
/// <summary> /// <summary>
/// Static constructor. /// Static constructor.
/// </summary> /// </summary>
static EnumUtils() static EnumUtils()
{ {
s_EnumValuesCache = new Dictionary<Type, object[]>(); s_EnumValuesCache = new Dictionary<Type, int[]>();
s_EnumFlagsCache = new Dictionary<Type, Dictionary<object, object[]>>(); s_EnumFlagsCache = new Dictionary<Type, Dictionary<int, int[]>>();
} }
/// <summary> /// <summary>
@@ -75,12 +75,12 @@ namespace ICD.Common.Utils
if (!IsFlagsEnum<T>()) if (!IsFlagsEnum<T>())
return GetValues<T>().Any(v => v.Equals(value)); return GetValues<T>().Any(v => v.Equals(value));
int valueInt = (int)GetUnderlyingValue(value); int valueInt = (int)(object)value;
// Check if all of the flag values are defined // Check if all of the flag values are defined
foreach (T flag in GetFlags(value)) foreach (T flag in GetFlags(value))
{ {
int flagInt = (int)GetUnderlyingValue(flag); int flagInt = (int)(object)flag;
valueInt = valueInt - flagInt; valueInt = valueInt - flagInt;
} }
@@ -89,23 +89,6 @@ namespace ICD.Common.Utils
#region Values #region Values
/// <summary>
/// Gets the underlying value of the enum.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static object GetUnderlyingValue<T>(T value)
{
if (!IsEnumType(typeof(T)))
throw new InvalidOperationException(string.Format("{0} is not an enum", typeof(T).Name));
#if SIMPLSHARP
return Convert.ChangeType(value, ToEnum(value).GetTypeCode(), CultureInfo.InvariantCulture);
#else
return Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType()));
#endif
}
/// <summary> /// <summary>
/// Gets the values from an enumeration. /// Gets the values from an enumeration.
/// </summary> /// </summary>
@@ -121,16 +104,16 @@ namespace ICD.Common.Utils
/// </summary> /// </summary>
/// <param name="type"></param> /// <param name="type"></param>
/// <returns></returns> /// <returns></returns>
public static IEnumerable<object> GetValues(Type type) public static IEnumerable<int> GetValues(Type type)
{ {
if (type == null) if (type == null)
throw new ArgumentNullException("type"); throw new ArgumentNullException("type");
// Reflection is slow and this method is called a lot, so we cache the results. // Reflection is slow and this method is called a lot, so we cache the results.
object[] cache; int[] cache;
if (!s_EnumValuesCache.TryGetValue(type, out cache)) if (!s_EnumValuesCache.TryGetValue(type, out cache))
{ {
cache = GetValuesUncached(type).ToArray(); cache = GetValuesUncached(type).Cast<int>().ToArray();
s_EnumValuesCache[type] = cache; s_EnumValuesCache[type] = cache;
} }
@@ -191,7 +174,7 @@ namespace ICD.Common.Utils
/// </summary> /// </summary>
/// <param name="type"></param> /// <param name="type"></param>
/// <returns></returns> /// <returns></returns>
public static IEnumerable<object> GetValuesExceptNone(Type type) public static IEnumerable<int> GetValuesExceptNone(Type type)
{ {
if (type == null) if (type == null)
throw new ArgumentNullException("type"); throw new ArgumentNullException("type");
@@ -199,7 +182,7 @@ namespace ICD.Common.Utils
if (!IsEnumType(type)) if (!IsEnumType(type))
throw new InvalidOperationException(string.Format("{0} is not an enum", type.Name)); throw new InvalidOperationException(string.Format("{0} is not an enum", type.Name));
return GetValues(type).Where(v => (int)v != 0); return GetValues(type).Where(v => v != 0);
} }
#endregion #endregion
@@ -284,21 +267,22 @@ namespace ICD.Common.Utils
throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value"); throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value");
Type type = typeof(T); Type type = typeof(T);
int valueInt = (int)(object)value;
Dictionary<object, object[]> cache; Dictionary<int, int[]> cache;
if (!s_EnumFlagsCache.TryGetValue(type, out cache)) if (!s_EnumFlagsCache.TryGetValue(type, out cache))
{ {
cache = new Dictionary<object, object[]>(); cache = new Dictionary<int, int[]>();
s_EnumFlagsCache[type] = cache; s_EnumFlagsCache[type] = cache;
} }
object[] flags; int[] flags;
if (!cache.TryGetValue(value, out flags)) if (!cache.TryGetValue(valueInt, out flags))
{ {
flags = GetValues<T>().Where(e => HasFlag(value, e)) flags = GetValues<T>().Where(e => HasFlag(value, e))
.Cast<object>() .Cast<int>()
.ToArray(); .ToArray();
cache[value] = flags; cache[valueInt] = flags;
} }
return flags.Cast<T>(); return flags.Cast<T>();