Added BitPosition method and changed generic constraints to use System.Enum

pull/30/head
David Hall 2018-12-17 14:34:18 -07:00
parent 4356d92c71
commit 16109f8336
1 changed files with 32 additions and 21 deletions

View File

@ -9,14 +9,29 @@ namespace Vanara.Extensions
/// <summary>Extensions for enumerated types.</summary> /// <summary>Extensions for enumerated types.</summary>
public static class EnumExtensions public static class EnumExtensions
{ {
/// <summary>Gets the bit position of a flag that has a single bit, starting at 0.</summary>
/// <typeparam name="T">The enumerated type.</typeparam>
/// <param name="flags">The enumerated value.</param>
/// <returns>The bit position, starting at 0, of the single bit flag specified in <paramref name="flags"/>.</returns>
/// <exception cref="ArgumentException">The flag value is zero and has no bit position.</exception>
/// <exception cref="ArithmeticException">The flag value has more than a single bit set.</exception>
public static byte BitPosition<T>(this T flags) where T : struct, System.Enum
{
CheckHasFlags<T>();
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);
}
/// <summary>Throws an exception if a flag value does not exist in a specified enumeration.</summary> /// <summary>Throws an exception if a flag value does not exist in a specified enumeration.</summary>
/// <typeparam name="T">The enumerated type.</typeparam> /// <typeparam name="T">The enumerated type.</typeparam>
/// <param name="value">The value to check.</param> /// <param name="value">The value to check.</param>
/// <param name="argName">Name of the argument to display in the exception. "value" is used if no value or <c>null</c> is supplied.</param> /// <param name="argName">Name of the argument to display in the exception. "value" is used if no value or <c>null</c> is supplied.</param>
/// <exception cref="InvalidEnumArgumentException"></exception> /// <exception cref="InvalidEnumArgumentException"></exception>
public static void CheckHasValue<T>(T value, string argName = null) public static void CheckHasValue<T>(T value, string argName = null) where T : struct, System.Enum
{ {
CheckIsEnum<T>();
if (IsFlags<T>()) if (IsFlags<T>())
{ {
var allFlags = 0L; var allFlags = 0L;
@ -35,15 +50,15 @@ namespace Vanara.Extensions
/// <param name="flags">The enumerated value.</param> /// <param name="flags">The enumerated value.</param>
/// <param name="flag">The flags to clear or unset.</param> /// <param name="flag">The flags to clear or unset.</param>
/// <returns>The resulting enumerated value after the <paramref name="flag"/> has been unset.</returns> /// <returns>The resulting enumerated value after the <paramref name="flag"/> has been unset.</returns>
public static T ClearFlags<T>(this T flags, T flag) where T : struct, IConvertible => flags.SetFlags(flag, false); public static T ClearFlags<T>(this T flags, T flag) where T : struct, System.Enum => flags.SetFlags(flag, false);
/// <summary>Combines enumerated list of values into a single enumerated value.</summary> /// <summary>Combines enumerated list of values into a single enumerated value.</summary>
/// <typeparam name="T">The enumerated type.</typeparam> /// <typeparam name="T">The enumerated type.</typeparam>
/// <param name="flags">The flags to combine.</param> /// <param name="flags">The flags to combine.</param>
/// <returns>A single enumerated value.</returns> /// <returns>A single enumerated value.</returns>
public static T CombineFlags<T>(this IEnumerable<T> flags) where T : struct, IConvertible public static T CombineFlags<T>(this IEnumerable<T> flags) where T : struct, System.Enum
{ {
CheckIsEnum<T>(true); CheckHasFlags<T>();
long lValue = 0; long lValue = 0;
foreach (var flag in flags) foreach (var flag in flags)
{ {
@ -57,9 +72,8 @@ namespace Vanara.Extensions
/// <typeparam name="T">The enumerated type.</typeparam> /// <typeparam name="T">The enumerated type.</typeparam>
/// <param name="value">The enumerated value.</param> /// <param name="value">The enumerated value.</param>
/// <returns>The description, or <c>null</c> if one is not set.</returns> /// <returns>The description, or <c>null</c> if one is not set.</returns>
public static string GetDescription<T>(this T value) where T : struct, IConvertible public static string GetDescription<T>(this T value) where T : struct, System.Enum
{ {
CheckIsEnum<T>();
// If this is flag, return flags if there are more than one. // If this is flag, return flags if there are more than one.
if (IsFlags<T>() && value.GetFlags().Count() > 1) if (IsFlags<T>() && value.GetFlags().Count() > 1)
return value.ToString(CultureInfo.InvariantCulture); return value.ToString(CultureInfo.InvariantCulture);
@ -78,9 +92,9 @@ namespace Vanara.Extensions
/// <typeparam name="T">The enumerated type.</typeparam> /// <typeparam name="T">The enumerated type.</typeparam>
/// <param name="value">The enumerated value.</param> /// <param name="value">The enumerated value.</param>
/// <returns>An enumeration of individual flags that compose the <paramref name="value"/>.</returns> /// <returns>An enumeration of individual flags that compose the <paramref name="value"/>.</returns>
public static IEnumerable<T> GetFlags<T>(this T value) where T : struct, IConvertible public static IEnumerable<T> GetFlags<T>(this T value) where T : struct, System.Enum
{ {
CheckIsEnum<T>(true); CheckHasFlags<T>();
foreach (T flag in Enum.GetValues(typeof(T))) foreach (T flag in Enum.GetValues(typeof(T)))
{ {
if (value.IsFlagSet(flag)) if (value.IsFlagSet(flag))
@ -93,9 +107,9 @@ namespace Vanara.Extensions
/// <param name="flags">The enumerated flag value.</param> /// <param name="flags">The enumerated flag value.</param>
/// <param name="flag">The flag value to check.</param> /// <param name="flag">The flag value to check.</param>
/// <returns><c>true</c> if is flag set; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if is flag set; otherwise, <c>false</c>.</returns>
public static bool IsFlagSet<T>(this T flags, T flag) where T : struct, IConvertible public static bool IsFlagSet<T>(this T flags, T flag) where T : struct, System.Enum
{ {
CheckIsEnum<T>(true); CheckHasFlags<T>();
var flagValue = Convert.ToInt64(flag); var flagValue = Convert.ToInt64(flag);
return (Convert.ToInt64(flags) & flagValue) == flagValue; return (Convert.ToInt64(flags) & flagValue) == flagValue;
} }
@ -104,9 +118,8 @@ namespace Vanara.Extensions
/// <typeparam name="T">The enumerated type.</typeparam> /// <typeparam name="T">The enumerated type.</typeparam>
/// <param name="value">The enumerated value.</param> /// <param name="value">The enumerated value.</param>
/// <returns><c>true</c> if the specified value is valid; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if the specified value is valid; otherwise, <c>false</c>.</returns>
public static bool IsValid<T>(this T value) where T : struct, IConvertible public static bool IsValid<T>(this T value) where T : struct, System.Enum
{ {
CheckIsEnum<T>();
if (IsFlags<T>()) if (IsFlags<T>())
{ {
long mask = 0, lValue = Convert.ToInt64(value); long mask = 0, lValue = Convert.ToInt64(value);
@ -122,9 +135,9 @@ namespace Vanara.Extensions
/// <param name="flags">A reference to an enumerated value.</param> /// <param name="flags">A reference to an enumerated value.</param>
/// <param name="flag">The flag to set or unset.</param> /// <param name="flag">The flag to set or unset.</param>
/// <param name="set">if set to <c>true</c> sets the flag; otherwise the flag is unset.</param> /// <param name="set">if set to <c>true</c> sets the flag; otherwise the flag is unset.</param>
public static void SetFlags<T>(ref T flags, T flag, bool set = true) where T : struct, IConvertible public static void SetFlags<T>(ref T flags, T flag, bool set = true) where T : struct, System.Enum
{ {
CheckIsEnum<T>(true); CheckHasFlags<T>();
var flagsValue = Convert.ToInt64(flags); var flagsValue = Convert.ToInt64(flags);
var flagValue = Convert.ToInt64(flag); var flagValue = Convert.ToInt64(flag);
if (set) if (set)
@ -140,7 +153,7 @@ namespace Vanara.Extensions
/// <param name="flag">The flag to set or unset.</param> /// <param name="flag">The flag to set or unset.</param>
/// <param name="set">if set to <c>true</c> sets the flag; otherwise the flag is unset.</param> /// <param name="set">if set to <c>true</c> sets the flag; otherwise the flag is unset.</param>
/// <returns>The resulting enumerated value after the <paramref name="flag"/> has been set or unset.</returns> /// <returns>The resulting enumerated value after the <paramref name="flag"/> has been set or unset.</returns>
public static T SetFlags<T>(this T flags, T flag, bool set = true) where T : struct, IConvertible public static T SetFlags<T>(this T flags, T flag, bool set = true) where T : struct, System.Enum
{ {
var ret = flags; var ret = flags;
SetFlags(ref ret, flag, set); SetFlags(ref ret, flag, set);
@ -153,18 +166,16 @@ namespace Vanara.Extensions
/// if set to <c>true</c> the check with also assert that the enumeration has the <see cref="FlagsAttribute"/> set and will throw an exception if not. /// if set to <c>true</c> the check with also assert that the enumeration has the <see cref="FlagsAttribute"/> set and will throw an exception if not.
/// </param> /// </param>
/// <exception cref="System.ArgumentException"></exception> /// <exception cref="System.ArgumentException"></exception>
private static void CheckIsEnum<T>(bool checkHasFlags = false) private static void CheckHasFlags<T>() where T : struct, System.Enum
{ {
if (!typeof(T).IsEnum) if (!IsFlags<T>())
throw new ArgumentException($"Type '{typeof(T).FullName}' is not an enum");
if (checkHasFlags && !IsFlags<T>())
throw new ArgumentException($"Type '{typeof(T).FullName}' doesn't have the 'Flags' attribute"); throw new ArgumentException($"Type '{typeof(T).FullName}' doesn't have the 'Flags' attribute");
} }
/// <summary>Determines whether this enumerations has the <see cref="FlagsAttribute"/> set.</summary> /// <summary>Determines whether this enumerations has the <see cref="FlagsAttribute"/> set.</summary>
/// <typeparam name="T">The enumerated type.</typeparam> /// <typeparam name="T">The enumerated type.</typeparam>
/// <returns><c>true</c> if this instance has the <see cref="FlagsAttribute"/> set; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if this instance has the <see cref="FlagsAttribute"/> set; otherwise, <c>false</c>.</returns>
private static bool IsFlags<T>() => Attribute.IsDefined(typeof(T), typeof(FlagsAttribute)); private static bool IsFlags<T>() where T : struct, System.Enum => Attribute.IsDefined(typeof(T), typeof(FlagsAttribute));
/*/// <summary>Returns an indication if the enumerated value is either defined or can be defined by a set of known flags.</summary> /*/// <summary>Returns an indication if the enumerated value is either defined or can be defined by a set of known flags.</summary>
/// <typeparam name="T">The enumerated type.</typeparam> /// <typeparam name="T">The enumerated type.</typeparam>