using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; namespace Vanara.Extensions { /// Extensions for enumerated types. public static class EnumExtensions { /// Gets the bit position of a flag that has a single bit, starting at 0. /// The enumerated type. /// The enumerated value. /// The bit position, starting at 0, of the single bit flag specified in . /// The flag value is zero and has no bit position. /// The flag value has more than a single bit set. public static byte BitPosition(this T flags) where T : struct, System.Enum { CheckHasFlags(); var flagValue = Convert.ToInt64(flags); if (flagValue == 0) throw new ArgumentException("The flag value is zero and has no bit position."); var r = Math.Log(flagValue, 2); if (r % 1 > 0) throw new ArithmeticException("The flag value has more than a single bit set."); return Convert.ToByte(r); } /// Throws an exception if a flag value does not exist in a specified enumeration. /// The enumerated type. /// The value to check. /// Name of the argument to display in the exception. "value" is used if no value or null is supplied. /// public static void CheckHasValue(T value, string argName = null) where T : struct, System.Enum { if (!IsValid(value)) throw new InvalidEnumArgumentException(argName ?? "value", Convert.ToInt32(value), typeof(T)); } /// Clears the specified flags from an enumerated value and returns the new value. /// The enumerated type. /// The enumerated value. /// The flags to clear or unset. /// The resulting enumerated value after the has been unset. public static T ClearFlags(this T flags, T flag) where T : struct, System.Enum => flags.SetFlags(flag, false); /// Combines enumerated list of values into a single enumerated value. /// The enumerated type. /// The flags to combine. /// A single enumerated value. public static T CombineFlags(this IEnumerable flags) where T : struct, System.Enum { CheckHasFlags(); long lValue = 0; foreach (var flag in flags) { var lFlag = Convert.ToInt64(flag); lValue |= lFlag; } return (T)Enum.ToObject(typeof(T), lValue); } /// Gets the description supplied by a if one is set. /// The enumerated type. /// The enumerated value. /// The description, or null if one is not set. public static string GetDescription(this T value) where T : struct, System.Enum { // If this is flag, return flags if there are more than one. if (IsFlags() && value.GetFlags().Count() > 1) return value.ToString(); // Get the name or description of the single enum value. var name = Enum.GetName(typeof(T), value); if (name != null) { var field = typeof(T).GetField(name); if (field != null && Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) is DescriptionAttribute attr) return attr.Description; } return name; } /// Gets the flags of an enumerated value as an enumerated list. /// The enumerated type. /// The enumerated value. /// An enumeration of individual flags that compose the . public static IEnumerable GetFlags(this T value) where T : struct, System.Enum { CheckHasFlags(); foreach (T flag in Enum.GetValues(typeof(T))) { if (value.IsFlagSet(flag)) yield return flag; } } /// Determines whether the enumerated flag value has the specified flag set. /// The enumerated type. /// The enumerated flag value. /// The flag value to check. /// true if is flag set; otherwise, false. public static bool IsFlagSet(this T flags, T flag) where T : struct, System.Enum { CheckHasFlags(); var flagValue = Convert.ToInt64(flag); return (Convert.ToInt64(flags) & flagValue) == flagValue; } /// Returns an indication if the enumerated value is either defined or can be defined by a set of known flags. /// The enumerated type. /// The enumerated value. /// true if the specified value is valid; otherwise, false. public static bool IsValid(this T value) where T : struct, System.Enum { if (IsFlags()) { long mask = 0, lValue = Convert.ToInt64(value); foreach (T flag in Enum.GetValues(typeof(T))) mask |= Convert.ToInt64(flag); return (mask & lValue) == lValue; } return Enum.IsDefined(typeof(T), value); } /// Set or unsets flags in a referenced enumerated value. /// The enumerated type. /// A reference to an enumerated value. /// The flag to set or unset. /// if set to true sets the flag; otherwise the flag is unset. public static void SetFlags(ref T flags, T flag, bool set = true) where T : struct, System.Enum { CheckHasFlags(); var flagsValue = Convert.ToInt64(flags); var flagValue = Convert.ToInt64(flag); if (set) flagsValue |= flagValue; else flagsValue &= (~flagValue); flags = (T)Enum.ToObject(typeof(T), flagsValue); } /// Set or unsets flags in an enumerated value and returns the new value. /// The enumerated type. /// The enumerated value. /// The flag to set or unset. /// if set to true sets the flag; otherwise the flag is unset. /// The resulting enumerated value after the has been set or unset. public static T SetFlags(this T flags, T flag, bool set = true) where T : struct, System.Enum { var ret = flags; SetFlags(ref ret, flag, set); return ret; } /// Converts an enumerated value to another fixed type. /// The enumerated type. /// The type of the result. /// The value to convert. /// The converted value. /// The size of TResult cannot be smaller than the size of TEnum. public static TResult To(this TEnum value) where TEnum : unmanaged, System.Enum where TResult : unmanaged, IConvertible { unsafe { if (sizeof(TResult) < sizeof(TEnum)) throw new ArgumentException("The size of TResult cannot be smaller than the size of TEnum."); return *(TResult*)(void*)&value; } } /// Converts a fixed type to an enumerated value. /// The type of the value to convert. /// The enumerated type. /// The value to convert. /// The converted value. /// The size of TEnum cannot be smaller than the size of TValue. public static TEnum ToEnum(this TValue value) where TEnum : unmanaged, System.Enum where TValue : unmanaged, IConvertible { unsafe { if (sizeof(TEnum) < sizeof(TValue)) throw new ArgumentException("The size of TEnum cannot be smaller than the size of TValue."); return *(TEnum*)(void*)&value; } } /// Converts a fixed type to an enumerated value. /// The enumerated type. /// The value to convert. /// The converted value. public static TEnum ToEnum(this byte value) where TEnum : unmanaged, System.Enum => ToEnum(value); /// Converts a fixed type to an enumerated value. /// The enumerated type. /// The value to convert. /// The converted value. public static TEnum ToEnum(this sbyte value) where TEnum : unmanaged, System.Enum => ToEnum(value); /// Converts a fixed type to an enumerated value. /// The enumerated type. /// The value to convert. /// The converted value. public static TEnum ToEnum(this ushort value) where TEnum : unmanaged, System.Enum => ToEnum(value); /// Converts a fixed type to an enumerated value. /// The enumerated type. /// The value to convert. /// The converted value. public static TEnum ToEnum(this short value) where TEnum : unmanaged, System.Enum => ToEnum(value); /// Converts a fixed type to an enumerated value. /// The enumerated type. /// The value to convert. /// The converted value. public static TEnum ToEnum(this uint value) where TEnum : unmanaged, System.Enum => ToEnum(value); /// Converts a fixed type to an enumerated value. /// The enumerated type. /// The value to convert. /// The converted value. public static TEnum ToEnum(this int value) where TEnum : unmanaged, System.Enum => ToEnum(value); /// Checks if represents an enumeration and throws an exception if not. /// The to validate. /// private static void CheckHasFlags() where T : struct, System.Enum { if (!IsFlags()) throw new ArgumentException($"Type '{typeof(T).FullName}' doesn't have the 'Flags' attribute"); } /// Determines whether this enumerations has the set. /// The enumerated type. /// true if this instance has the set; otherwise, false. private static bool IsFlags() where T : struct, System.Enum => Attribute.IsDefined(typeof(T), typeof(FlagsAttribute)); /*/// Returns an indication if the enumerated value is either defined or can be defined by a set of known flags. /// The enumerated type. /// The enumerated value. /// The valid values. /// true if the specified value is valid; otherwise, false. private static bool IsValid(this T value, params T[] validValues) where T : struct, IConvertible { CheckIsEnum(); foreach (var vval in validValues) if (value.Equals(vval)) return true; return false; }*/ } }