mirror of
https://github.com/ICDSystems/ICD.Common.Utils.git
synced 2026-02-16 05:05:05 +00:00
perf: Massive performance improvements to enum HasFlag and HasFlags extensions
This commit is contained in:
@@ -273,20 +273,6 @@ namespace ICD.Common.Utils.Tests
|
|||||||
Assert.AreEqual(eTestFlagsEnum.None, outputB);
|
Assert.AreEqual(eTestFlagsEnum.None, outputB);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void ToEnumGenericTest()
|
|
||||||
{
|
|
||||||
Assert.AreEqual(eTestEnum.A, EnumUtils.ToEnum(eTestEnum.A));
|
|
||||||
Assert.AreNotEqual(eTestEnum.B, EnumUtils.ToEnum(eTestEnum.A));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void ToEnumTest()
|
|
||||||
{
|
|
||||||
Assert.AreEqual(eTestEnum.A, EnumUtils.ToEnum((object)eTestEnum.A));
|
|
||||||
Assert.AreNotEqual(eTestEnum.B, EnumUtils.ToEnum((object)eTestEnum.A));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -253,6 +253,7 @@ namespace ICD.Common.Utils.Tests.Xml
|
|||||||
[TestCase("<Test><Child>A</Child></Test>", "Child", true, eTestEnum.A)]
|
[TestCase("<Test><Child>A</Child></Test>", "Child", true, eTestEnum.A)]
|
||||||
[TestCase("<Test><Child>A, B</Child></Test>", "Child", true, eTestEnum.A | eTestEnum.B)]
|
[TestCase("<Test><Child>A, B</Child></Test>", "Child", true, eTestEnum.A | eTestEnum.B)]
|
||||||
public void ReadChildElementContentAsEnumTest<T>(string xml, string childElement, bool ignoreCase, T expected)
|
public void ReadChildElementContentAsEnumTest<T>(string xml, string childElement, bool ignoreCase, T expected)
|
||||||
|
where T : struct, IConvertible
|
||||||
{
|
{
|
||||||
Assert.AreEqual(expected, XmlUtils.ReadChildElementContentAsEnum<T>(xml, childElement, ignoreCase));
|
Assert.AreEqual(expected, XmlUtils.ReadChildElementContentAsEnum<T>(xml, childElement, ignoreCase));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,12 +25,13 @@ namespace ICD.Common.Utils
|
|||||||
s_EnumFlagsCache = new Dictionary<Type, Dictionary<int, object>>();
|
s_EnumFlagsCache = new Dictionary<Type, Dictionary<int, object>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Validation
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns true if the given type is an enum.
|
/// Returns true if the given type is an enum.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static bool IsEnumType<T>()
|
public static bool IsEnumType<T>()
|
||||||
where T : struct, IConvertible
|
|
||||||
{
|
{
|
||||||
return IsEnumType(typeof(T));
|
return IsEnumType(typeof(T));
|
||||||
}
|
}
|
||||||
@@ -56,9 +57,34 @@ namespace ICD.Common.Utils
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static bool IsEnum<T>(T value)
|
public static bool IsEnum<T>(T value)
|
||||||
where T : struct, IConvertible
|
|
||||||
{
|
{
|
||||||
return IsEnumType(value.GetType());
|
return value != null && IsEnumType(value.GetType());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the given enum type has the Flags attribute set.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool IsFlagsEnum<T>()
|
||||||
|
{
|
||||||
|
return IsFlagsEnum(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");
|
||||||
|
|
||||||
|
return type
|
||||||
|
#if !SIMPLSHARP
|
||||||
|
.GetTypeInfo()
|
||||||
|
#endif
|
||||||
|
.IsDefined(typeof(FlagsAttribute), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -73,21 +99,30 @@ namespace ICD.Common.Utils
|
|||||||
if (!IsEnumType<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>())
|
T all = GetFlagsAllValue<T>();
|
||||||
return GetValues<T>().Any(v => v.Equals(value));
|
return HasFlags(all, value);
|
||||||
|
|
||||||
int valueInt = (int)(object)value;
|
|
||||||
|
|
||||||
// Check if all of the flag values are defined
|
|
||||||
foreach (T flag in GetFlags(value))
|
|
||||||
{
|
|
||||||
int flagInt = (int)(object)flag;
|
|
||||||
valueInt = valueInt - flagInt;
|
|
||||||
}
|
|
||||||
|
|
||||||
return valueInt == 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the given value is defined as part of the given enum type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool IsDefined(Type type, int value)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
throw new ArgumentNullException("type");
|
||||||
|
|
||||||
|
if (!IsEnumType(type))
|
||||||
|
throw new InvalidOperationException(string.Format("{0} is not an enum", type.Name));
|
||||||
|
|
||||||
|
int all = GetFlagsAllValue(type);
|
||||||
|
return HasFlags(all, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Values
|
#region Values
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -117,7 +152,10 @@ namespace ICD.Common.Utils
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IEnumerable<int> GetValues(Type type)
|
public static IEnumerable<int> GetValues(Type type)
|
||||||
{
|
{
|
||||||
return GetValuesUncached(type).Cast<int>();
|
if (type == null)
|
||||||
|
throw new ArgumentNullException("type");
|
||||||
|
|
||||||
|
return GetValuesUncached(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -134,8 +172,11 @@ namespace ICD.Common.Utils
|
|||||||
/// 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>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private static IEnumerable<object> GetValuesUncached(Type type)
|
private static IEnumerable<int> GetValuesUncached(Type type)
|
||||||
{
|
{
|
||||||
|
if (type == null)
|
||||||
|
throw new ArgumentNullException("type");
|
||||||
|
|
||||||
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));
|
||||||
|
|
||||||
@@ -146,7 +187,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<int>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -166,6 +208,9 @@ namespace ICD.Common.Utils
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IEnumerable<int> GetValuesExceptNone(Type type)
|
public static IEnumerable<int> GetValuesExceptNone(Type type)
|
||||||
{
|
{
|
||||||
|
if (type == null)
|
||||||
|
throw new ArgumentNullException("type");
|
||||||
|
|
||||||
return GetValues(type).Except(0);
|
return GetValues(type).Except(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,30 +218,6 @@ namespace ICD.Common.Utils
|
|||||||
|
|
||||||
#region Flags
|
#region Flags
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns true if the given enum type has the Flags attribute set.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static bool IsFlagsEnum<T>()
|
|
||||||
where T : struct, IConvertible
|
|
||||||
{
|
|
||||||
return IsFlagsEnum(typeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns true if the given enum type has the Flags attribute set.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static bool IsFlagsEnum(Type type)
|
|
||||||
{
|
|
||||||
return type
|
|
||||||
#if !SIMPLSHARP
|
|
||||||
.GetTypeInfo()
|
|
||||||
#endif
|
|
||||||
.IsDefined(typeof(FlagsAttribute), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the overlapping values of the given enum flags.
|
/// Gets the overlapping values of the given enum flags.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -206,14 +227,32 @@ namespace ICD.Common.Utils
|
|||||||
public static T GetFlagsIntersection<T>(params T[] values)
|
public static T GetFlagsIntersection<T>(params T[] values)
|
||||||
where T : struct, IConvertible
|
where T : struct, IConvertible
|
||||||
{
|
{
|
||||||
|
if (values == null)
|
||||||
|
throw new ArgumentNullException("values");
|
||||||
|
|
||||||
if (values.Length == 0)
|
if (values.Length == 0)
|
||||||
return default(T);
|
return default(T);
|
||||||
|
|
||||||
int output = (int)(object)values.First();
|
int output = 0;
|
||||||
foreach (T item in values.Skip(1))
|
bool first = true;
|
||||||
output &= (int)(object)item;
|
|
||||||
|
|
||||||
return (T)Enum.ToObject(typeof(T), output);
|
foreach (T value in values)
|
||||||
|
{
|
||||||
|
if (first)
|
||||||
|
{
|
||||||
|
output = (int)(object)value;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
output &= (int)(object)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output == 0)
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (T)(object)output;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -229,7 +268,7 @@ namespace ICD.Common.Utils
|
|||||||
int aInt = (int)(object)a;
|
int aInt = (int)(object)a;
|
||||||
int bInt = (int)(object)b;
|
int bInt = (int)(object)b;
|
||||||
|
|
||||||
return (T)Enum.ToObject(typeof(T), aInt & bInt);
|
return (T)(object)(aInt & bInt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -328,6 +367,22 @@ namespace ICD.Common.Utils
|
|||||||
return (T)Enum.ToObject(typeof(T), output);
|
return (T)Enum.ToObject(typeof(T), output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an enum value of the given type with every flag set.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static int GetFlagsAllValue(Type type)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
throw new ArgumentNullException("type");
|
||||||
|
|
||||||
|
if (!IsEnumType(type))
|
||||||
|
throw new ArgumentException(string.Format("{0} is not an enum", type.Name));
|
||||||
|
|
||||||
|
return GetValuesUncached(type).Aggregate(0, (current, value) => current | value);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns true if the enum contains the given flag.
|
/// Returns true if the enum contains the given flag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -347,6 +402,17 @@ namespace ICD.Common.Utils
|
|||||||
return HasFlags(value, flag);
|
return HasFlags(value, flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the enum contains the given flag.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="flag"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool HasFlag(int value, int flag)
|
||||||
|
{
|
||||||
|
return HasFlags(value, flag);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns true if the enum contains all of the given flags.
|
/// Returns true if the enum contains all of the given flags.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -366,7 +432,18 @@ namespace ICD.Common.Utils
|
|||||||
int a = (int)(object)value;
|
int a = (int)(object)value;
|
||||||
int b = (int)(object)flags;
|
int b = (int)(object)flags;
|
||||||
|
|
||||||
return (a & b) == b;
|
return HasFlags(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the enum contains the given flag.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="flags"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool HasFlags(int value, int flags)
|
||||||
|
{
|
||||||
|
return (value & flags) == flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -471,6 +548,29 @@ namespace ICD.Common.Utils
|
|||||||
throw new FormatException(message);
|
throw new FormatException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shorthand for parsing string to enum.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <param name="ignoreCase"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static int Parse(Type type, string data, bool ignoreCase)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
throw new ArgumentNullException("type");
|
||||||
|
|
||||||
|
if (!IsEnumType(type))
|
||||||
|
throw new ArgumentException(string.Format("{0} is not an enum", type.Name));
|
||||||
|
|
||||||
|
int output;
|
||||||
|
if (TryParse(type, data, ignoreCase, out output))
|
||||||
|
return output;
|
||||||
|
|
||||||
|
string message = string.Format("Failed to parse {0} as {1}", StringUtils.ToRepresentation(data), type.Name);
|
||||||
|
throw new FormatException(message);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Shorthand for parsing a string to enum. Returns false if the parse failed.
|
/// Shorthand for parsing a string to enum. Returns false if the parse failed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -498,6 +598,35 @@ namespace ICD.Common.Utils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shorthand for parsing a string to enum. Returns false if the parse failed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <param name="ignoreCase"></param>
|
||||||
|
/// <param name="result"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool TryParse(Type type, string data, bool ignoreCase, out int result)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
throw new ArgumentNullException("type");
|
||||||
|
|
||||||
|
if (!IsEnumType(type))
|
||||||
|
throw new ArgumentException(string.Format("{0} is not an enum", type.Name));
|
||||||
|
|
||||||
|
result = 0;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
result = (int)Enum.Parse(type, data, ignoreCase);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Shorthand for parsing string to enum.
|
/// Shorthand for parsing string to enum.
|
||||||
/// Will fail if the resulting value is not defined as part of the enum.
|
/// Will fail if the resulting value is not defined as part of the enum.
|
||||||
@@ -530,6 +659,40 @@ namespace ICD.Common.Utils
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shorthand for parsing string to enum.
|
||||||
|
/// Will fail if the resulting value is not defined as part of the enum.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <param name="ignoreCase"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static int ParseStrict(Type type, string data, bool ignoreCase)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
throw new ArgumentNullException("type");
|
||||||
|
|
||||||
|
if (!IsEnumType(type))
|
||||||
|
throw new ArgumentException(string.Format("{0} is not an enum", type.Name));
|
||||||
|
|
||||||
|
int output;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
output = Parse(type, data, ignoreCase);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new FormatException(
|
||||||
|
string.Format("Failed to parse {0} as {1}", StringUtils.ToRepresentation(data), type.Name), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsDefined(type, output))
|
||||||
|
throw new ArgumentOutOfRangeException(string.Format("{0} is not a valid {1}", output, type.Name));
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Shorthand for parsing a string to enum. Returns false if the parse failed.
|
/// Shorthand for parsing a string to enum. Returns false if the parse failed.
|
||||||
/// Will fail if the resulting value is not defined as part of the enum.
|
/// Will fail if the resulting value is not defined as part of the enum.
|
||||||
|
|||||||
Reference in New Issue
Block a user