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 (!Math.Abs(r).Equals(r)) 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;
}*/
}
}