perf: Adding contrains for enum methods, faster HasFlag/s checks

This commit is contained in:
Chris Cameron
2018-07-26 10:56:08 -04:00
parent 1a87ce9f00
commit 668994be18
5 changed files with 71 additions and 66 deletions

View File

@@ -30,6 +30,7 @@ namespace ICD.Common.Utils
/// </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));
} }
@@ -55,9 +56,9 @@ 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
{ {
// ReSharper disable once CompareNonConstrainedGenericWithNull return IsEnumType(value.GetType());
return value != null && IsEnumType(value.GetType());
} }
/// <summary> /// <summary>
@@ -67,6 +68,7 @@ namespace ICD.Common.Utils
/// <param name="value"></param> /// <param name="value"></param>
/// <returns></returns> /// <returns></returns>
public static bool IsDefined<T>(T value) public static bool IsDefined<T>(T value)
where T : struct, IConvertible
{ {
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));
@@ -94,6 +96,7 @@ namespace ICD.Common.Utils
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <returns></returns> /// <returns></returns>
public static IEnumerable<T> GetValues<T>() public static IEnumerable<T> GetValues<T>()
where T : struct, IConvertible
{ {
Type type = typeof(T); Type type = typeof(T);
@@ -122,6 +125,7 @@ namespace ICD.Common.Utils
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
private static IEnumerable<T> GetValuesUncached<T>() private static IEnumerable<T> GetValuesUncached<T>()
where T : struct, IConvertible
{ {
return GetValuesUncached(typeof(T)).Cast<T>(); return GetValuesUncached(typeof(T)).Cast<T>();
} }
@@ -151,6 +155,7 @@ namespace ICD.Common.Utils
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <returns></returns> /// <returns></returns>
public static IEnumerable<T> GetValuesExceptNone<T>() public static IEnumerable<T> GetValuesExceptNone<T>()
where T : struct, IConvertible
{ {
return GetFlagsExceptNone<T>(); return GetFlagsExceptNone<T>();
} }
@@ -174,6 +179,7 @@ namespace ICD.Common.Utils
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <returns></returns> /// <returns></returns>
public static bool IsFlagsEnum<T>() public static bool IsFlagsEnum<T>()
where T : struct, IConvertible
{ {
return IsFlagsEnum(typeof(T)); return IsFlagsEnum(typeof(T));
} }
@@ -198,6 +204,7 @@ namespace ICD.Common.Utils
/// <param name="values"></param> /// <param name="values"></param>
/// <returns></returns> /// <returns></returns>
public static T GetFlagsIntersection<T>(params T[] values) public static T GetFlagsIntersection<T>(params T[] values)
where T : struct, IConvertible
{ {
if (values.Length == 0) if (values.Length == 0)
return default(T); return default(T);
@@ -217,6 +224,7 @@ namespace ICD.Common.Utils
/// <param name="b"></param> /// <param name="b"></param>
/// <returns></returns> /// <returns></returns>
public static T GetFlagsIntersection<T>(T a, T b) public static T GetFlagsIntersection<T>(T a, T b)
where T : struct, IConvertible
{ {
int aInt = (int)(object)a; int aInt = (int)(object)a;
int bInt = (int)(object)b; int bInt = (int)(object)b;
@@ -231,10 +239,10 @@ namespace ICD.Common.Utils
/// <param name="value"></param> /// <param name="value"></param>
/// <returns></returns> /// <returns></returns>
public static IEnumerable<T> GetFlags<T>(T value) public static IEnumerable<T> GetFlags<T>(T value)
where T : struct, IConvertible
{ {
if (!IsEnum(value)) if (!IsEnum(value))
// ReSharper disable once CompareNonConstrainedGenericWithNull throw new ArgumentException(string.Format("{0} is not an enum", 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; int valueInt = (int)(object)value;
@@ -262,6 +270,7 @@ namespace ICD.Common.Utils
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <returns></returns> /// <returns></returns>
public static IEnumerable<T> GetFlagsExceptNone<T>() public static IEnumerable<T> GetFlagsExceptNone<T>()
where T : struct, IConvertible
{ {
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));
@@ -277,10 +286,10 @@ namespace ICD.Common.Utils
/// <param name="value"></param> /// <param name="value"></param>
/// <returns></returns> /// <returns></returns>
public static IEnumerable<T> GetFlagsExceptNone<T>(T value) public static IEnumerable<T> GetFlagsExceptNone<T>(T value)
where T : struct, IConvertible
{ {
if (!IsEnum(value)) if (!IsEnum(value))
// ReSharper disable once CompareNonConstrainedGenericWithNull throw new ArgumentException(string.Format("{0} is not an enum", value.GetType().Name), "value");
throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value");
return GetFlags(value).Except(default(T)); return GetFlags(value).Except(default(T));
} }
@@ -295,10 +304,10 @@ namespace ICD.Common.Utils
/// <param name="value"></param> /// <param name="value"></param>
/// <returns></returns> /// <returns></returns>
public static IEnumerable<T> GetAllFlagCombinationsExceptNone<T>(T value) public static IEnumerable<T> GetAllFlagCombinationsExceptNone<T>(T value)
where T : struct, IConvertible
{ {
if (!IsEnum(value)) if (!IsEnum(value))
// ReSharper disable once CompareNonConstrainedGenericWithNull throw new ArgumentException(string.Format("{0} is not an enum", 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));
@@ -310,6 +319,7 @@ namespace ICD.Common.Utils
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <returns></returns> /// <returns></returns>
public static T GetFlagsAllValue<T>() public static T GetFlagsAllValue<T>()
where T : struct, IConvertible
{ {
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));
@@ -326,16 +336,15 @@ namespace ICD.Common.Utils
/// <param name="flag"></param> /// <param name="flag"></param>
/// <returns></returns> /// <returns></returns>
public static bool HasFlag<T>(T value, T flag) public static bool HasFlag<T>(T value, T flag)
where T : struct, IConvertible
{ {
if (!IsEnum(value)) if (!IsEnum(value))
// ReSharper disable once CompareNonConstrainedGenericWithNull throw new ArgumentException(string.Format("{0} is not an enum", value.GetType().Name), "value");
throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value");
if (!IsEnum(flag)) if (!IsEnum(flag))
// ReSharper disable once CompareNonConstrainedGenericWithNull throw new ArgumentException(string.Format("{0} is not an enum", flag.GetType().Name), "flag");
throw new ArgumentException(string.Format("{0} is not an enum", flag == null ? "NULL" : flag.GetType().Name), "flag");
return ToEnum(value).HasFlag(ToEnum(flag)); return HasFlags(value, flag);
} }
/// <summary> /// <summary>
@@ -346,16 +355,18 @@ namespace ICD.Common.Utils
/// <param name="flags"></param> /// <param name="flags"></param>
/// <returns></returns> /// <returns></returns>
public static bool HasFlags<T>(T value, T flags) public static bool HasFlags<T>(T value, T flags)
where T : struct, IConvertible
{ {
if (!IsEnum(value)) if (!IsEnum(value))
// ReSharper disable once CompareNonConstrainedGenericWithNull throw new ArgumentException(string.Format("{0} is not an enum", value.GetType().Name), "value");
throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value");
if (!IsEnum(flags)) if (!IsEnum(flags))
// ReSharper disable once CompareNonConstrainedGenericWithNull throw new ArgumentException(string.Format("{0} is not an enum", flags.GetType().Name), "flags");
throw new ArgumentException(string.Format("{0} is not an enum", flags == null ? "NULL" : flags.GetType().Name), "flags");
return ToEnum(value).HasFlags(ToEnum(flags)); int a = (int)(object)value;
int b = (int)(object)flags;
return (a & b) == b;
} }
/// <summary> /// <summary>
@@ -365,10 +376,10 @@ namespace ICD.Common.Utils
/// <param name="value"></param> /// <param name="value"></param>
/// <returns></returns> /// <returns></returns>
public static bool HasSingleFlag<T>(T value) public static bool HasSingleFlag<T>(T value)
where T : struct, IConvertible
{ {
if (!IsEnum(value)) if (!IsEnum(value))
// ReSharper disable once CompareNonConstrainedGenericWithNull throw new ArgumentException(string.Format("{0} is not an enum", value.GetType().Name), "value");
throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value");
return HasAnyFlags(value) && !HasMultipleFlags(value); return HasAnyFlags(value) && !HasMultipleFlags(value);
} }
@@ -379,10 +390,10 @@ namespace ICD.Common.Utils
/// <param name="value"></param> /// <param name="value"></param>
/// <returns></returns> /// <returns></returns>
public static bool HasMultipleFlags<T>(T value) public static bool HasMultipleFlags<T>(T value)
where T : struct, IConvertible
{ {
if (!IsEnum(value)) if (!IsEnum(value))
// ReSharper disable once CompareNonConstrainedGenericWithNull throw new ArgumentException(string.Format("{0} is not an enum", value.GetType().Name), "value");
throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value");
return HasMultipleFlags((int)(object)value); return HasMultipleFlags((int)(object)value);
} }
@@ -405,6 +416,7 @@ namespace ICD.Common.Utils
/// <returns></returns> /// <returns></returns>
[PublicAPI] [PublicAPI]
public static bool HasAnyFlags<T>(T value) public static bool HasAnyFlags<T>(T value)
where T : struct, IConvertible
{ {
return HasAnyFlags((int)(object)value); return HasAnyFlags((int)(object)value);
} }
@@ -428,6 +440,7 @@ namespace ICD.Common.Utils
/// <returns></returns> /// <returns></returns>
[PublicAPI] [PublicAPI]
public static bool HasAnyFlags<T>(T value, T other) public static bool HasAnyFlags<T>(T value, T other)
where T : struct, IConvertible
{ {
T intersection = GetFlagsIntersection(value, other); T intersection = GetFlagsIntersection(value, other);
return HasAnyFlags(intersection); return HasAnyFlags(intersection);
@@ -445,6 +458,7 @@ namespace ICD.Common.Utils
/// <param name="ignoreCase"></param> /// <param name="ignoreCase"></param>
/// <returns></returns> /// <returns></returns>
public static T Parse<T>(string data, bool ignoreCase) public static T Parse<T>(string data, bool ignoreCase)
where T : struct, IConvertible
{ {
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));
@@ -466,6 +480,7 @@ namespace ICD.Common.Utils
/// <param name="result"></param> /// <param name="result"></param>
/// <returns></returns> /// <returns></returns>
public static bool TryParse<T>(string data, bool ignoreCase, out T result) public static bool TryParse<T>(string data, bool ignoreCase, out T result)
where T : struct, IConvertible
{ {
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));
@@ -492,6 +507,7 @@ namespace ICD.Common.Utils
/// <param name="ignoreCase"></param> /// <param name="ignoreCase"></param>
/// <returns></returns> /// <returns></returns>
public static T ParseStrict<T>(string data, bool ignoreCase) public static T ParseStrict<T>(string data, bool ignoreCase)
where T : struct, IConvertible
{ {
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));
@@ -524,6 +540,7 @@ namespace ICD.Common.Utils
/// <param name="result"></param> /// <param name="result"></param>
/// <returns></returns> /// <returns></returns>
public static bool TryParseStrict<T>(string data, bool ignoreCase, out T result) public static bool TryParseStrict<T>(string data, bool ignoreCase, out T result)
where T : struct, IConvertible
{ {
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));
@@ -541,21 +558,6 @@ namespace ICD.Common.Utils
} }
} }
/// <summary>
/// Converts the given enum value to an Enum.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <returns></returns>
public static Enum ToEnum<T>(T value)
{
if (!IsEnum(value))
// ReSharper disable once CompareNonConstrainedGenericWithNull
throw new ArgumentException(string.Format("{0} is not an enum", value == null ? "NULL" : value.GetType().Name), "value");
return (Enum)(object)value;
}
#endregion #endregion
} }
} }

View File

@@ -12,51 +12,49 @@ namespace ICD.Common.Utils.Extensions
/// <param name="value">Flag to check for</param> /// <param name="value">Flag to check for</param>
/// <returns></returns> /// <returns></returns>
[PublicAPI] [PublicAPI]
public static bool HasFlag(this Enum extends, Enum value) public static bool HasFlag<T>(this T extends, T value)
where T : struct, IConvertible
{ {
if (extends == null) if (!EnumUtils.IsEnum(extends))
throw new ArgumentNullException("extends"); throw new ArgumentException(string.Format("{0} is not an enum", extends.GetType().Name), "extends");
if (value == null) if (!EnumUtils.IsEnum(value))
throw new ArgumentNullException("value"); throw new ArgumentException(string.Format("{0} is not an enum", value.GetType().Name), "value");
if (EnumUtils.HasMultipleFlags(value)) return EnumUtils.HasFlag(extends, value);
throw new ArgumentException("Value has multiple flags", "value");
return extends.HasFlags(value);
} }
/// <summary> /// <summary>
/// Check to see if a flags enumeration has all of the given flags set. /// Check to see if a flags enumeration has all of the given flags set.
/// </summary> /// </summary>
/// <param name="extends"></param> /// <param name="extends"></param>
/// <param name="value"></param> /// <param name="values"></param>
/// <returns></returns> /// <returns></returns>
[PublicAPI] [PublicAPI]
public static bool HasFlags(this Enum extends, Enum value) public static bool HasFlags<T>(this T extends, T values)
where T : struct, IConvertible
{ {
if (extends == null) if (!EnumUtils.IsEnum(extends))
throw new ArgumentNullException("extends"); throw new ArgumentException(string.Format("{0} is not an enum", extends.GetType().Name), "extends");
if (value == null) if (!EnumUtils.IsEnum(values))
throw new ArgumentNullException("value"); throw new ArgumentException(string.Format("{0} is not an enum", values.GetType().Name), "values");
// Not as good as the .NET 4 version of this function, but should be good enough return EnumUtils.HasFlags(extends, values);
if (extends.GetType() != value.GetType())
{
string message = string.Format("Enumeration type mismatch. The flag is of type '{0}', was expecting '{1}'.",
value.GetType(), extends.GetType());
throw new ArgumentException(message);
}
int num = (int)(object)value;
return ((int)(object)extends & num) == num;
} }
public static ushort ToUShort(this Enum extends) /// <summary>
/// Returns the enum value as a
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI("S+")]
public static ushort ToUShort<T>(this T extends)
where T : struct, IConvertible
{ {
if (extends == null) if (!EnumUtils.IsEnum(extends))
throw new ArgumentNullException("extends"); throw new ArgumentException(string.Format("{0} is not an enum", extends.GetType().Name), "extends");
return (ushort)(object)extends; return (ushort)(object)extends;
} }

View File

@@ -155,6 +155,7 @@ namespace ICD.Common.Utils.Extensions
/// <returns></returns> /// <returns></returns>
[PublicAPI] [PublicAPI]
public static T GetValueAsEnum<T>(this JsonReader extends) public static T GetValueAsEnum<T>(this JsonReader extends)
where T : struct, IConvertible
{ {
if (extends == null) if (extends == null)
throw new ArgumentNullException("extends"); throw new ArgumentNullException("extends");

View File

@@ -392,6 +392,7 @@ namespace ICD.Common.Utils.Xml
/// <returns></returns> /// <returns></returns>
[PublicAPI] [PublicAPI]
public static T ReadElementContentAsEnum<T>(this IcdXmlReader extends, bool ignoreCase) public static T ReadElementContentAsEnum<T>(this IcdXmlReader extends, bool ignoreCase)
where T : struct, IConvertible
{ {
if (extends == null) if (extends == null)
throw new ArgumentNullException("extends"); throw new ArgumentNullException("extends");

View File

@@ -422,6 +422,7 @@ namespace ICD.Common.Utils.Xml
/// <returns></returns> /// <returns></returns>
[PublicAPI] [PublicAPI]
public static T ReadChildElementContentAsEnum<T>(string xml, string childElement, bool ignoreCase) public static T ReadChildElementContentAsEnum<T>(string xml, string childElement, bool ignoreCase)
where T : struct, IConvertible
{ {
if (!EnumUtils.IsEnumType<T>()) if (!EnumUtils.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));
@@ -608,7 +609,7 @@ namespace ICD.Common.Utils.Xml
/// <returns></returns> /// <returns></returns>
[PublicAPI] [PublicAPI]
public static T? TryReadChildElementContentAsEnum<T>(string xml, string childElement, bool ignoreCase) public static T? TryReadChildElementContentAsEnum<T>(string xml, string childElement, bool ignoreCase)
where T : struct where T : struct, IConvertible
{ {
if (!EnumUtils.IsEnumType<T>()) if (!EnumUtils.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));
@@ -628,6 +629,7 @@ namespace ICD.Common.Utils.Xml
/// <returns></returns> /// <returns></returns>
[PublicAPI] [PublicAPI]
public static bool TryReadChildElementContentAsEnum<T>(string xml, string childElement, bool ignoreCase, out T output) public static bool TryReadChildElementContentAsEnum<T>(string xml, string childElement, bool ignoreCase, out T output)
where T : struct, IConvertible
{ {
if (!EnumUtils.IsEnumType<T>()) if (!EnumUtils.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));
@@ -731,6 +733,7 @@ namespace ICD.Common.Utils.Xml
/// <returns></returns> /// <returns></returns>
[PublicAPI] [PublicAPI]
public static T ReadElementContentAsEnum<T>(string xml, bool ignoreCase) public static T ReadElementContentAsEnum<T>(string xml, bool ignoreCase)
where T : struct, IConvertible
{ {
if (!EnumUtils.IsEnumType<T>()) if (!EnumUtils.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));