mirror of
https://github.com/ICDSystems/ICD.Common.Utils.git
synced 2026-02-15 20:54:58 +00:00
perf: Further reducing enum boxing
This commit is contained in:
@@ -37,16 +37,6 @@ namespace ICD.Common.Utils.Tests
|
|||||||
Assert.AreEqual(eTestEnum.C, values[3]);
|
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<ArgumentNullException>(() => EnumUtils.IsEnumType(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void IsEnumTypeGenericTest()
|
public void IsEnumTypeGenericTest()
|
||||||
{
|
{
|
||||||
@@ -63,7 +53,7 @@ namespace ICD.Common.Utils.Tests
|
|||||||
Assert.IsTrue(EnumUtils.IsEnum(eTestFlagsEnum.A));
|
Assert.IsTrue(EnumUtils.IsEnum(eTestFlagsEnum.A));
|
||||||
Assert.IsTrue(EnumUtils.IsEnum(eTestEnum.A as object));
|
Assert.IsTrue(EnumUtils.IsEnum(eTestEnum.A as object));
|
||||||
Assert.IsTrue(EnumUtils.IsEnum(eTestEnum.A as Enum));
|
Assert.IsTrue(EnumUtils.IsEnum(eTestEnum.A as Enum));
|
||||||
Assert.IsFalse(EnumUtils.IsEnum(null));
|
Assert.IsFalse(EnumUtils.IsEnum<Enum>(null));
|
||||||
Assert.IsFalse(EnumUtils.IsEnum(""));
|
Assert.IsFalse(EnumUtils.IsEnum(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,13 +81,6 @@ namespace ICD.Common.Utils.Tests
|
|||||||
Assert.IsTrue(values.Contains(eTestEnum.C));
|
Assert.IsTrue(values.Contains(eTestEnum.C));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void GetNoneValueGenericTest()
|
|
||||||
{
|
|
||||||
Assert.AreEqual(eTestEnum.None, EnumUtils.GetNoneValue<eTestEnum>());
|
|
||||||
Assert.AreEqual(eTestFlagsEnum.None, EnumUtils.GetNoneValue<eTestFlagsEnum>());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void GetValuesExceptNoneGenericTest()
|
public void GetValuesExceptNoneGenericTest()
|
||||||
{
|
{
|
||||||
@@ -110,18 +93,6 @@ namespace ICD.Common.Utils.Tests
|
|||||||
Assert.IsTrue(values.Contains(eTestEnum.C));
|
Assert.IsTrue(values.Contains(eTestEnum.C));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void GetValuesExceptNoneTest()
|
|
||||||
{
|
|
||||||
eTestEnum[] values = EnumUtils.GetValuesExceptNone(typeof(eTestEnum)).Cast<eTestEnum>().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
|
#endregion
|
||||||
|
|
||||||
#region Flags
|
#region Flags
|
||||||
@@ -133,13 +104,6 @@ namespace ICD.Common.Utils.Tests
|
|||||||
Assert.IsTrue(EnumUtils.IsFlagsEnum<eTestFlagsEnum>());
|
Assert.IsTrue(EnumUtils.IsFlagsEnum<eTestFlagsEnum>());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void IsFlagsEnumTest()
|
|
||||||
{
|
|
||||||
Assert.IsFalse(EnumUtils.IsFlagsEnum(typeof(eTestEnum)));
|
|
||||||
Assert.IsTrue(EnumUtils.IsFlagsEnum(typeof(eTestFlagsEnum)));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void GetFlagsIntersectionGenericTest()
|
public void GetFlagsIntersectionGenericTest()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,33 +13,16 @@ namespace ICD.Common.Utils
|
|||||||
{
|
{
|
||||||
public static class EnumUtils
|
public static class EnumUtils
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<Type, int[]> s_EnumValuesCache;
|
private static readonly Dictionary<Type, object> s_EnumValuesCache;
|
||||||
private static readonly Dictionary<Type, Dictionary<int, int[]>> s_EnumFlagsCache;
|
private static readonly Dictionary<Type, Dictionary<int, object>> s_EnumFlagsCache;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Static constructor.
|
/// Static constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
static EnumUtils()
|
static EnumUtils()
|
||||||
{
|
{
|
||||||
s_EnumValuesCache = new Dictionary<Type, int[]>();
|
s_EnumValuesCache = new Dictionary<Type, object>();
|
||||||
s_EnumFlagsCache = new Dictionary<Type, Dictionary<int, int[]>>();
|
s_EnumFlagsCache = new Dictionary<Type, Dictionary<int, object>>();
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns true if the given type is an enum.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -51,6 +34,22 @@ namespace ICD.Common.Utils
|
|||||||
return IsEnumType(typeof(T));
|
return IsEnumType(typeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the given type is an enum.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static bool IsEnumType(Type type)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
throw new ArgumentNullException("type");
|
||||||
|
|
||||||
|
return type
|
||||||
|
#if !SIMPLSHARP
|
||||||
|
.GetTypeInfo()
|
||||||
|
#endif
|
||||||
|
.IsEnum || type.IsAssignableTo(typeof(Enum));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns true if the given value is an enum.
|
/// Returns true if the given value is an enum.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -58,7 +57,7 @@ namespace ICD.Common.Utils
|
|||||||
public static bool IsEnum<T>(T value)
|
public static bool IsEnum<T>(T value)
|
||||||
{
|
{
|
||||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||||
return value != null && IsEnumType<T>();
|
return value != null && IsEnumType(value.GetType());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -69,7 +68,7 @@ namespace ICD.Common.Utils
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static bool IsDefined<T>(T value)
|
public static bool IsDefined<T>(T value)
|
||||||
{
|
{
|
||||||
if (!IsEnumType(typeof(T)))
|
if (!IsEnumType<T>())
|
||||||
throw new InvalidOperationException(string.Format("{0} is not an enum", typeof(T).Name));
|
throw new InvalidOperationException(string.Format("{0} is not an enum", typeof(T).Name));
|
||||||
|
|
||||||
if (!IsFlagsEnum<T>())
|
if (!IsFlagsEnum<T>())
|
||||||
@@ -96,41 +95,28 @@ namespace ICD.Common.Utils
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IEnumerable<T> GetValues<T>()
|
public static IEnumerable<T> GetValues<T>()
|
||||||
{
|
{
|
||||||
return GetValues(typeof(T)).Cast<T>();
|
Type type = typeof(T);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the values from an enumeration.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static IEnumerable<int> GetValues(Type type)
|
|
||||||
{
|
|
||||||
if (type == null)
|
|
||||||
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.
|
||||||
int[] cache;
|
object cache;
|
||||||
if (!s_EnumValuesCache.TryGetValue(type, out cache))
|
if (!s_EnumValuesCache.TryGetValue(type, out cache))
|
||||||
{
|
{
|
||||||
cache = GetValuesUncached(type).Cast<int>().ToArray();
|
cache = GetValuesUncached<T>().ToArray();
|
||||||
s_EnumValuesCache[type] = cache;
|
s_EnumValuesCache[type] = cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
return cache;
|
return cache as T[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the values from an enumeration without performing any caching. This is slow because of reflection.
|
/// Gets the values from an enumeration without performing any caching. This is slow because of reflection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type"></param>
|
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private static IEnumerable<object> GetValuesUncached(Type type)
|
private static IEnumerable<T> GetValuesUncached<T>()
|
||||||
{
|
{
|
||||||
if (type == null)
|
Type type = typeof(T);
|
||||||
throw new ArgumentNullException("type");
|
|
||||||
|
|
||||||
if (!IsEnumType(type))
|
if (!IsEnumType<T>())
|
||||||
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 type
|
return type
|
||||||
@@ -140,20 +126,8 @@ namespace ICD.Common.Utils
|
|||||||
.GetTypeInfo()
|
.GetTypeInfo()
|
||||||
#endif
|
#endif
|
||||||
.GetFields(BindingFlags.Static | BindingFlags.Public)
|
.GetFields(BindingFlags.Static | BindingFlags.Public)
|
||||||
.Select(x => x.GetValue(null));
|
.Select(x => x.GetValue(null))
|
||||||
}
|
.Cast<T>();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the 0 value for the given enum type.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static T GetNoneValue<T>()
|
|
||||||
{
|
|
||||||
if (!IsEnumType(typeof(T)))
|
|
||||||
throw new InvalidOperationException(string.Format("{0} is not an enum", typeof(T).Name));
|
|
||||||
|
|
||||||
return (T)(object)0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -163,26 +137,7 @@ namespace ICD.Common.Utils
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IEnumerable<T> GetValuesExceptNone<T>()
|
public static IEnumerable<T> GetValuesExceptNone<T>()
|
||||||
{
|
{
|
||||||
if (!IsEnumType(typeof(T)))
|
return GetFlagsExceptNone<T>();
|
||||||
throw new InvalidOperationException(string.Format("{0} is not an enum", typeof(T).Name));
|
|
||||||
|
|
||||||
return GetValuesExceptNone(typeof(T)).Cast<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the values from an enumeration except the 0 value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static IEnumerable<int> 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -199,22 +154,7 @@ namespace ICD.Common.Utils
|
|||||||
if (!IsEnumType<T>())
|
if (!IsEnumType<T>())
|
||||||
throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name));
|
throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name));
|
||||||
|
|
||||||
return IsFlagsEnum(typeof(T));
|
return typeof(T)
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns true if the given enum type has the Flags attribute set.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
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
|
|
||||||
#if !SIMPLSHARP
|
#if !SIMPLSHARP
|
||||||
.GetTypeInfo()
|
.GetTypeInfo()
|
||||||
#endif
|
#endif
|
||||||
@@ -230,7 +170,7 @@ namespace ICD.Common.Utils
|
|||||||
public static T GetFlagsIntersection<T>(params T[] values)
|
public static T GetFlagsIntersection<T>(params T[] values)
|
||||||
{
|
{
|
||||||
if (values.Length == 0)
|
if (values.Length == 0)
|
||||||
return GetNoneValue<T>();
|
return default(T);
|
||||||
|
|
||||||
int output = (int)(object)values.First();
|
int output = (int)(object)values.First();
|
||||||
foreach (T item in values.Skip(1))
|
foreach (T item in values.Skip(1))
|
||||||
@@ -269,23 +209,21 @@ namespace ICD.Common.Utils
|
|||||||
Type type = typeof(T);
|
Type type = typeof(T);
|
||||||
int valueInt = (int)(object)value;
|
int valueInt = (int)(object)value;
|
||||||
|
|
||||||
Dictionary<int, int[]> cache;
|
Dictionary<int, object> cache;
|
||||||
if (!s_EnumFlagsCache.TryGetValue(type, out cache))
|
if (!s_EnumFlagsCache.TryGetValue(type, out cache))
|
||||||
{
|
{
|
||||||
cache = new Dictionary<int, int[]>();
|
cache = new Dictionary<int, object>();
|
||||||
s_EnumFlagsCache[type] = cache;
|
s_EnumFlagsCache[type] = cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
int[] flags;
|
object flags;
|
||||||
if (!cache.TryGetValue(valueInt, out flags))
|
if (!cache.TryGetValue(valueInt, out flags))
|
||||||
{
|
{
|
||||||
flags = GetValues<T>().Where(e => HasFlag(value, e))
|
flags = GetValues<T>().Where(e => HasFlag(value, e)).ToArray();
|
||||||
.Cast<int>()
|
|
||||||
.ToArray();
|
|
||||||
cache[valueInt] = flags;
|
cache[valueInt] = flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
return flags.Cast<T>();
|
return flags as T[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -314,8 +252,7 @@ namespace ICD.Common.Utils
|
|||||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||||
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");
|
||||||
|
|
||||||
T none = GetNoneValue<T>();
|
return GetFlags(value).Except(default(T));
|
||||||
return GetFlags(value).Except(none);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -333,7 +270,7 @@ namespace ICD.Common.Utils
|
|||||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||||
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");
|
||||||
|
|
||||||
int maxEnumValue = (GetValues<T>().Max(v => (int)(object)v) * 2) -1 ;
|
int maxEnumValue = (GetValues<T>().Max(v => (int)(object)v) * 2) -1;
|
||||||
return Enumerable.Range(1, maxEnumValue).Select(i => (T)(object)i ).Where(v => HasFlags(value, v));
|
return Enumerable.Range(1, maxEnumValue).Select(i => (T)(object)i ).Where(v => HasFlags(value, v));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user