mirror of
https://github.com/ICDSystems/ICD.Common.Utils.git
synced 2026-02-11 18:54:55 +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);
|
||||
}
|
||||
|
||||
[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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,6 +253,7 @@ namespace ICD.Common.Utils.Tests.Xml
|
||||
[TestCase("<Test><Child>A</Child></Test>", "Child", true, eTestEnum.A)]
|
||||
[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)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
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>>();
|
||||
}
|
||||
|
||||
#region Validation
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given type is an enum.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static bool IsEnumType<T>()
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
return IsEnumType(typeof(T));
|
||||
}
|
||||
@@ -56,9 +57,34 @@ namespace ICD.Common.Utils
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
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>
|
||||
@@ -73,21 +99,30 @@ namespace ICD.Common.Utils
|
||||
if (!IsEnumType<T>())
|
||||
throw new InvalidOperationException(string.Format("{0} is not an enum", typeof(T).Name));
|
||||
|
||||
if (!IsFlagsEnum<T>())
|
||||
return GetValues<T>().Any(v => v.Equals(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;
|
||||
T all = GetFlagsAllValue<T>();
|
||||
return HasFlags(all, value);
|
||||
}
|
||||
|
||||
/// <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
|
||||
|
||||
/// <summary>
|
||||
@@ -117,7 +152,10 @@ namespace ICD.Common.Utils
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<int> GetValues(Type type)
|
||||
{
|
||||
return GetValuesUncached(type).Cast<int>();
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
return GetValuesUncached(type);
|
||||
}
|
||||
|
||||
/// <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.
|
||||
/// </summary>
|
||||
/// <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))
|
||||
throw new InvalidOperationException(string.Format("{0} is not an enum", type.Name));
|
||||
|
||||
@@ -146,7 +187,8 @@ namespace ICD.Common.Utils
|
||||
.GetTypeInfo()
|
||||
#endif
|
||||
.GetFields(BindingFlags.Static | BindingFlags.Public)
|
||||
.Select(x => x.GetValue(null));
|
||||
.Select(x => x.GetValue(null))
|
||||
.Cast<int>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -166,6 +208,9 @@ namespace ICD.Common.Utils
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<int> GetValuesExceptNone(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
return GetValues(type).Except(0);
|
||||
}
|
||||
|
||||
@@ -173,30 +218,6 @@ namespace ICD.Common.Utils
|
||||
|
||||
#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>
|
||||
/// Gets the overlapping values of the given enum flags.
|
||||
/// </summary>
|
||||
@@ -206,14 +227,32 @@ namespace ICD.Common.Utils
|
||||
public static T GetFlagsIntersection<T>(params T[] values)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
if (values == null)
|
||||
throw new ArgumentNullException("values");
|
||||
|
||||
if (values.Length == 0)
|
||||
return default(T);
|
||||
|
||||
int output = (int)(object)values.First();
|
||||
foreach (T item in values.Skip(1))
|
||||
output &= (int)(object)item;
|
||||
int output = 0;
|
||||
bool first = true;
|
||||
|
||||
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>
|
||||
@@ -229,7 +268,7 @@ namespace ICD.Common.Utils
|
||||
int aInt = (int)(object)a;
|
||||
int bInt = (int)(object)b;
|
||||
|
||||
return (T)Enum.ToObject(typeof(T), aInt & bInt);
|
||||
return (T)(object)(aInt & bInt);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -328,6 +367,22 @@ namespace ICD.Common.Utils
|
||||
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>
|
||||
/// Returns true if the enum contains the given flag.
|
||||
/// </summary>
|
||||
@@ -347,6 +402,17 @@ namespace ICD.Common.Utils
|
||||
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>
|
||||
/// Returns true if the enum contains all of the given flags.
|
||||
/// </summary>
|
||||
@@ -366,7 +432,18 @@ namespace ICD.Common.Utils
|
||||
int a = (int)(object)value;
|
||||
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>
|
||||
@@ -471,6 +548,29 @@ namespace ICD.Common.Utils
|
||||
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>
|
||||
/// Shorthand for parsing a string to enum. Returns false if the parse failed.
|
||||
/// </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>
|
||||
/// Shorthand for parsing string to 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;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// 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.
|
||||
|
||||
Reference in New Issue
Block a user