From 668994be1832ba6b2645f478861f5964ca6cb7c3 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Thu, 26 Jul 2018 10:56:08 -0400 Subject: [PATCH] perf: Adding contrains for enum methods, faster HasFlag/s checks --- ICD.Common.Utils/EnumUtils.cs | 76 ++++++++++--------- ICD.Common.Utils/Extensions/EnumExtensions.cs | 54 +++++++------ ICD.Common.Utils/Extensions/JsonExtensions.cs | 1 + ICD.Common.Utils/Xml/XmlReaderExtensions.cs | 1 + ICD.Common.Utils/Xml/XmlUtils.cs | 5 +- 5 files changed, 71 insertions(+), 66 deletions(-) diff --git a/ICD.Common.Utils/EnumUtils.cs b/ICD.Common.Utils/EnumUtils.cs index de233ce..729f5d6 100644 --- a/ICD.Common.Utils/EnumUtils.cs +++ b/ICD.Common.Utils/EnumUtils.cs @@ -30,6 +30,7 @@ namespace ICD.Common.Utils /// /// public static bool IsEnumType() + where T : struct, IConvertible { return IsEnumType(typeof(T)); } @@ -55,9 +56,9 @@ namespace ICD.Common.Utils /// /// public static bool IsEnum(T value) + where T : struct, IConvertible { -// ReSharper disable once CompareNonConstrainedGenericWithNull - return value != null && IsEnumType(value.GetType()); + return IsEnumType(value.GetType()); } /// @@ -67,6 +68,7 @@ namespace ICD.Common.Utils /// /// public static bool IsDefined(T value) + where T : struct, IConvertible { if (!IsEnumType()) throw new InvalidOperationException(string.Format("{0} is not an enum", typeof(T).Name)); @@ -94,6 +96,7 @@ namespace ICD.Common.Utils /// /// public static IEnumerable GetValues() + where T : struct, IConvertible { Type type = typeof(T); @@ -122,6 +125,7 @@ namespace ICD.Common.Utils /// /// private static IEnumerable GetValuesUncached() + where T : struct, IConvertible { return GetValuesUncached(typeof(T)).Cast(); } @@ -151,6 +155,7 @@ namespace ICD.Common.Utils /// /// public static IEnumerable GetValuesExceptNone() + where T : struct, IConvertible { return GetFlagsExceptNone(); } @@ -174,6 +179,7 @@ namespace ICD.Common.Utils /// /// public static bool IsFlagsEnum() + where T : struct, IConvertible { return IsFlagsEnum(typeof(T)); } @@ -198,6 +204,7 @@ namespace ICD.Common.Utils /// /// public static T GetFlagsIntersection(params T[] values) + where T : struct, IConvertible { if (values.Length == 0) return default(T); @@ -217,6 +224,7 @@ namespace ICD.Common.Utils /// /// public static T GetFlagsIntersection(T a, T b) + where T : struct, IConvertible { int aInt = (int)(object)a; int bInt = (int)(object)b; @@ -231,10 +239,10 @@ namespace ICD.Common.Utils /// /// public static IEnumerable GetFlags(T value) + where T : struct, IConvertible { if (!IsEnum(value)) -// 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.GetType().Name), "value"); Type type = typeof(T); int valueInt = (int)(object)value; @@ -262,6 +270,7 @@ namespace ICD.Common.Utils /// /// public static IEnumerable GetFlagsExceptNone() + where T : struct, IConvertible { if (!IsEnumType()) throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name)); @@ -277,10 +286,10 @@ namespace ICD.Common.Utils /// /// public static IEnumerable GetFlagsExceptNone(T value) + where T : struct, IConvertible { if (!IsEnum(value)) -// 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.GetType().Name), "value"); return GetFlags(value).Except(default(T)); } @@ -295,10 +304,10 @@ namespace ICD.Common.Utils /// /// public static IEnumerable GetAllFlagCombinationsExceptNone(T value) + where T : struct, IConvertible { if (!IsEnum(value)) - // 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.GetType().Name), "value"); int maxEnumValue = (GetValues().Max(v => (int)(object)v) * 2) -1; return Enumerable.Range(1, maxEnumValue).Select(i => (T)(object)i ).Where(v => HasFlags(value, v)); @@ -310,6 +319,7 @@ namespace ICD.Common.Utils /// /// public static T GetFlagsAllValue() + where T : struct, IConvertible { if (!IsEnumType()) throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name)); @@ -326,16 +336,15 @@ namespace ICD.Common.Utils /// /// public static bool HasFlag(T value, T flag) + where T : struct, IConvertible { if (!IsEnum(value)) -// 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.GetType().Name), "value"); if (!IsEnum(flag)) -// ReSharper disable once CompareNonConstrainedGenericWithNull - throw new ArgumentException(string.Format("{0} is not an enum", flag == null ? "NULL" : flag.GetType().Name), "flag"); + throw new ArgumentException(string.Format("{0} is not an enum", flag.GetType().Name), "flag"); - return ToEnum(value).HasFlag(ToEnum(flag)); + return HasFlags(value, flag); } /// @@ -346,16 +355,18 @@ namespace ICD.Common.Utils /// /// public static bool HasFlags(T value, T flags) + where T : struct, IConvertible { if (!IsEnum(value)) -// 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.GetType().Name), "value"); if (!IsEnum(flags)) -// ReSharper disable once CompareNonConstrainedGenericWithNull - throw new ArgumentException(string.Format("{0} is not an enum", flags == null ? "NULL" : flags.GetType().Name), "flags"); + throw new ArgumentException(string.Format("{0} is not an enum", flags.GetType().Name), "flags"); - return ToEnum(value).HasFlags(ToEnum(flags)); + int a = (int)(object)value; + int b = (int)(object)flags; + + return (a & b) == b; } /// @@ -365,10 +376,10 @@ namespace ICD.Common.Utils /// /// public static bool HasSingleFlag(T value) + where T : struct, IConvertible { if (!IsEnum(value)) -// 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.GetType().Name), "value"); return HasAnyFlags(value) && !HasMultipleFlags(value); } @@ -379,10 +390,10 @@ namespace ICD.Common.Utils /// /// public static bool HasMultipleFlags(T value) + where T : struct, IConvertible { if (!IsEnum(value)) -// 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.GetType().Name), "value"); return HasMultipleFlags((int)(object)value); } @@ -405,6 +416,7 @@ namespace ICD.Common.Utils /// [PublicAPI] public static bool HasAnyFlags(T value) + where T : struct, IConvertible { return HasAnyFlags((int)(object)value); } @@ -428,6 +440,7 @@ namespace ICD.Common.Utils /// [PublicAPI] public static bool HasAnyFlags(T value, T other) + where T : struct, IConvertible { T intersection = GetFlagsIntersection(value, other); return HasAnyFlags(intersection); @@ -445,6 +458,7 @@ namespace ICD.Common.Utils /// /// public static T Parse(string data, bool ignoreCase) + where T : struct, IConvertible { if (!IsEnumType()) throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name)); @@ -466,6 +480,7 @@ namespace ICD.Common.Utils /// /// public static bool TryParse(string data, bool ignoreCase, out T result) + where T : struct, IConvertible { if (!IsEnumType()) throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name)); @@ -492,6 +507,7 @@ namespace ICD.Common.Utils /// /// public static T ParseStrict(string data, bool ignoreCase) + where T : struct, IConvertible { if (!IsEnumType()) throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name)); @@ -524,6 +540,7 @@ namespace ICD.Common.Utils /// /// public static bool TryParseStrict(string data, bool ignoreCase, out T result) + where T : struct, IConvertible { if (!IsEnumType()) throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name)); @@ -541,21 +558,6 @@ namespace ICD.Common.Utils } } - /// - /// Converts the given enum value to an Enum. - /// - /// - /// - /// - public static Enum ToEnum(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 } } diff --git a/ICD.Common.Utils/Extensions/EnumExtensions.cs b/ICD.Common.Utils/Extensions/EnumExtensions.cs index f3bceb2..a9e4ce9 100644 --- a/ICD.Common.Utils/Extensions/EnumExtensions.cs +++ b/ICD.Common.Utils/Extensions/EnumExtensions.cs @@ -12,51 +12,49 @@ namespace ICD.Common.Utils.Extensions /// Flag to check for /// [PublicAPI] - public static bool HasFlag(this Enum extends, Enum value) + public static bool HasFlag(this T extends, T value) + where T : struct, IConvertible { - if (extends == null) - throw new ArgumentNullException("extends"); + if (!EnumUtils.IsEnum(extends)) + throw new ArgumentException(string.Format("{0} is not an enum", extends.GetType().Name), "extends"); - if (value == null) - throw new ArgumentNullException("value"); + if (!EnumUtils.IsEnum(value)) + throw new ArgumentException(string.Format("{0} is not an enum", value.GetType().Name), "value"); - if (EnumUtils.HasMultipleFlags(value)) - throw new ArgumentException("Value has multiple flags", "value"); - - return extends.HasFlags(value); + return EnumUtils.HasFlag(extends, value); } /// /// Check to see if a flags enumeration has all of the given flags set. /// /// - /// + /// /// [PublicAPI] - public static bool HasFlags(this Enum extends, Enum value) + public static bool HasFlags(this T extends, T values) + where T : struct, IConvertible { - if (extends == null) - throw new ArgumentNullException("extends"); + if (!EnumUtils.IsEnum(extends)) + throw new ArgumentException(string.Format("{0} is not an enum", extends.GetType().Name), "extends"); - if (value == null) - throw new ArgumentNullException("value"); + if (!EnumUtils.IsEnum(values)) + 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 - 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; + return EnumUtils.HasFlags(extends, values); } - public static ushort ToUShort(this Enum extends) + /// + /// Returns the enum value as a + /// + /// + /// + /// + [PublicAPI("S+")] + public static ushort ToUShort(this T extends) + where T : struct, IConvertible { - if (extends == null) - throw new ArgumentNullException("extends"); + if (!EnumUtils.IsEnum(extends)) + throw new ArgumentException(string.Format("{0} is not an enum", extends.GetType().Name), "extends"); return (ushort)(object)extends; } diff --git a/ICD.Common.Utils/Extensions/JsonExtensions.cs b/ICD.Common.Utils/Extensions/JsonExtensions.cs index 0332c91..5bffe77 100644 --- a/ICD.Common.Utils/Extensions/JsonExtensions.cs +++ b/ICD.Common.Utils/Extensions/JsonExtensions.cs @@ -155,6 +155,7 @@ namespace ICD.Common.Utils.Extensions /// [PublicAPI] public static T GetValueAsEnum(this JsonReader extends) + where T : struct, IConvertible { if (extends == null) throw new ArgumentNullException("extends"); diff --git a/ICD.Common.Utils/Xml/XmlReaderExtensions.cs b/ICD.Common.Utils/Xml/XmlReaderExtensions.cs index e7c7f79..613a9c6 100644 --- a/ICD.Common.Utils/Xml/XmlReaderExtensions.cs +++ b/ICD.Common.Utils/Xml/XmlReaderExtensions.cs @@ -392,6 +392,7 @@ namespace ICD.Common.Utils.Xml /// [PublicAPI] public static T ReadElementContentAsEnum(this IcdXmlReader extends, bool ignoreCase) + where T : struct, IConvertible { if (extends == null) throw new ArgumentNullException("extends"); diff --git a/ICD.Common.Utils/Xml/XmlUtils.cs b/ICD.Common.Utils/Xml/XmlUtils.cs index 1dd300a..226bc1a 100644 --- a/ICD.Common.Utils/Xml/XmlUtils.cs +++ b/ICD.Common.Utils/Xml/XmlUtils.cs @@ -422,6 +422,7 @@ namespace ICD.Common.Utils.Xml /// [PublicAPI] public static T ReadChildElementContentAsEnum(string xml, string childElement, bool ignoreCase) + where T : struct, IConvertible { if (!EnumUtils.IsEnumType()) throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name)); @@ -608,7 +609,7 @@ namespace ICD.Common.Utils.Xml /// [PublicAPI] public static T? TryReadChildElementContentAsEnum(string xml, string childElement, bool ignoreCase) - where T : struct + where T : struct, IConvertible { if (!EnumUtils.IsEnumType()) throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name)); @@ -628,6 +629,7 @@ namespace ICD.Common.Utils.Xml /// [PublicAPI] public static bool TryReadChildElementContentAsEnum(string xml, string childElement, bool ignoreCase, out T output) + where T : struct, IConvertible { if (!EnumUtils.IsEnumType()) throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name)); @@ -731,6 +733,7 @@ namespace ICD.Common.Utils.Xml /// [PublicAPI] public static T ReadElementContentAsEnum(string xml, bool ignoreCase) + where T : struct, IConvertible { if (!EnumUtils.IsEnumType()) throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name));