C# extension methods to ease dealing with Enum types.
namespace TomsToolbox.Core
{
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using JetBrains.Annotations;
#if NETSTANDARD1_0
using System.Reflection;
#endif
/// <summary>
/// Extension methods to ease dealing with <see cref="Enum"/> types.
/// </summary>
public static class EnumExtensions
{
/// <summary>
/// Determines whether any of the specified flags is set on the specified value.
/// </summary>
/// <typeparam name="T">The <see cref="Enum"/> type.</typeparam>
/// <param name="value">The value.</param>
/// <param name="flag">The flag.</param>
/// <returns>True if any of the specified flags is set.</returns>
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Flag", Justification = "Dealing with flags")]
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "flag", Justification = "Dealing with flags")]
public static bool IsAnyFlagSet<T>(this T value, T flag) where T : struct
{
VerifyIsFlagsEnum<T>();
var lValue = ToInt64(value);
var lFlags = ToInt64(flag);
return (lValue & lFlags) != 0;
}
/// <summary>
/// Determines whether all of the specified flags are set on the specified value.
/// </summary>
/// <typeparam name="T">The <see cref="Enum"/> type.</typeparam>
/// <param name="value">The value.</param>
/// <param name="flag">The flag.</param>
/// <returns>True if all of the specified flags are set.</returns>
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Flags", Justification = "Dealing with flags")]
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "flag", Justification = "Dealing with flags")]
public static bool AreAllFlagsSet<T>(this T value, T flag) where T : struct
{
VerifyIsFlagsEnum<T>();
var lValue = ToInt64(value);
var lFlags = ToInt64(flag);
return (lValue & lFlags) == lFlags;
}
/// <summary>
/// Gets the individual flags set on the specified value.
/// </summary>
/// <typeparam name="T">The <see cref="Enum"/> type.</typeparam>
/// <param name="value">The value.</param>
/// <returns>The individual flags set on the specified value.</returns>
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Flags", Justification = "Dealing with flags")]
[NotNull]
public static IEnumerable<T> GetFlags<T>(this T value) where T : struct
{
VerifyIsFlagsEnum<T>();
return Enum.GetValues(typeof(T)).Cast<T>().Where(flag => value.IsAnyFlagSet(flag));
}
/// <summary>
/// Sets the specified flags on the specified value on or off.
/// </summary>
/// <typeparam name="T">The <see cref="Enum"/> type.</typeparam>
/// <param name="value">The value.</param>
/// <param name="flags">The flags.</param>
/// <param name="on">If set to <c>true</c>, the flags are set, otherwise the flags are cleared.</param>
/// <returns>The value with the specified flags set or cleared.</returns>
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Flags", Justification = "Dealing with flags")]
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "flags", Justification = "Dealing with flags")]
public static T SetFlags<T>(this T value, T flags, bool on) where T : struct
{
VerifyIsFlagsEnum<T>();
var lValue = ToInt64(value);
var lFlag = ToInt64(flags);
if (on)
{
lValue |= lFlag;
}
else
{
lValue &= (~lFlag);
}
// ReSharper disable once PossibleNullReferenceException
return (T)Enum.ToObject(typeof(T), lValue);
}
/// <summary>
/// Sets the specified flags on the specified value.
/// </summary>
/// <typeparam name="T">The <see cref="Enum"/> type.</typeparam>
/// <param name="value">The value.</param>
/// <param name="flags">The flags.</param>
/// <returns>The value with the specified flags set.</returns>
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Flags", Justification = "Dealing with flags")]
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "flags", Justification = "Dealing with flags")]
public static T SetFlags<T>(this T value, T flags) where T : struct
{
return value.SetFlags(flags, true);
}
/// <summary>
/// Clears the specified flags on the specified value.
/// </summary>
/// <typeparam name="T">The <see cref="Enum"/> type.</typeparam>
/// <param name="value">The value.</param>
/// <param name="flags">The flags.</param>
/// <returns>The value with the specified flags cleared.</returns>
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Flags", Justification = "Dealing with flags")]
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "flags", Justification = "Dealing with flags")]
public static T ClearFlags<T>(this T value, T flags) where T : struct
{
return value.SetFlags(flags, false);
}
/// <summary>
/// Combines the flags into a single <see cref="Enum"/>.
/// </summary>
/// <typeparam name="T">The <see cref="Enum"/> type.</typeparam>
/// <param name="flags">The flags.</param>
/// <returns>The combined flags.</returns>
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Flags", Justification = "Dealing with flags")]
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "flags", Justification = "Dealing with flags")]
public static T CombineFlags<T>([NotNull] this IEnumerable<T> flags) where T : struct
{
VerifyIsFlagsEnum<T>();
var lValue = flags
.Select(flag => ToInt64(flag))
.Aggregate<long, long>(0, (current, lFlag) => current | lFlag);
// ReSharper disable once PossibleNullReferenceException
return (T)Enum.ToObject(typeof(T), lValue);
}
/// <summary>
/// Converts an integer value into an <see cref="Enum"/>.
/// </summary>
/// <typeparam name="T">The <see cref="Enum"/> type</typeparam>
/// <param name="value">The value.</param>
/// <returns>The <see cref="Enum"/> corresponding to the value. If the value is not valid for the given <see cref="Enum"/>, the default value for the <see cref="Enum"/> is returned.</returns>
public static T ToEnum<T>(this int value)
where T : struct
{
return InternalToEnum<T>(value);
}
/// <summary>
/// Converts an unsigned integer value into an <see cref="Enum"/>.
/// </summary>
/// <typeparam name="T">The <see cref="Enum"/> type</typeparam>
/// <param name="value">The value.</param>
/// <returns>The <see cref="Enum"/> corresponding to the value. If the value is not valid for the given <see cref="Enum"/>, the default value for the <see cref="Enum"/> is returned.</returns>
[CLSCompliant(false)]
public static T ToEnum<T>(this uint value)
where T : struct
{
return InternalToEnum<T>(value);
}
/// <summary>
/// Converts a short value into an <see cref="Enum"/>.
/// </summary>
/// <typeparam name="T">The <see cref="Enum"/> type</typeparam>
/// <param name="value">The value.</param>
/// <returns>The <see cref="Enum"/> corresponding to the value. If the value is not valid for the given <see cref="Enum"/>, the default value for the <see cref="Enum"/> is returned.</returns>
public static T ToEnum<T>(this short value)
where T : struct
{
return InternalToEnum<T>(value);
}
/// <summary>
/// Converts an unsigned short value into an <see cref="Enum"/>.
/// </summary>
/// <typeparam name="T">The <see cref="Enum"/> type</typeparam>
/// <param name="value">The value.</param>
/// <returns>The <see cref="Enum"/> corresponding to the value. If the value is not valid for the given <see cref="Enum"/>, the default value for the <see cref="Enum"/> is returned.</returns>
[CLSCompliant(false)]
public static T ToEnum<T>(this ushort value)
where T : struct
{
return InternalToEnum<T>(value);
}
/// <summary>
/// Converts a long value into an <see cref="Enum"/>.
/// </summary>
/// <typeparam name="T">The <see cref="Enum"/> type</typeparam>
/// <param name="value">The value.</param>
/// <returns>The <see cref="Enum"/> corresponding to the value. If the value is not valid for the given <see cref="Enum"/>, the default value for the <see cref="Enum"/> is returned.</returns>
public static T ToEnum<T>(this long value)
where T : struct
{
return InternalToEnum<T>(value);
}
/// <summary>
/// Converts an unsigned long value into an <see cref="Enum"/>.
/// </summary>
/// <typeparam name="T">The <see cref="Enum"/> type</typeparam>
/// <param name="value">The value.</param>
/// <returns>The <see cref="Enum"/> corresponding to the value. If the value is not valid for the given <see cref="Enum"/>, the default value for the <see cref="Enum"/> is returned.</returns>
[CLSCompliant(false)]
public static T ToEnum<T>(this ulong value)
where T : struct
{
return InternalToEnum<T>(value);
}
/// <summary>
/// Converts an integer value into an enum.
/// </summary>
/// <typeparam name="T">The enum type</typeparam>
/// <param name="value">The value.</param>
/// <returns>The enum corresponding to the value. If the value is not valid for the given enum, the default value for the enum is returned.</returns>
public static T? ToEnum<T>(this int? value)
where T : struct
{
return InternalToNullableEnum<T>(value);
}
/// <summary>
/// Converts an unsigned integer value into an enum.
/// </summary>
/// <typeparam name="T">The enum type</typeparam>
/// <param name="value">The value.</param>
/// <returns>The enum corresponding to the value. If the value is not valid for the given enum, the default value for the enum is returned.</returns>
[CLSCompliant(false)]
public static T? ToEnum<T>(this uint? value)
where T : struct
{
return InternalToNullableEnum<T>(value);
}
/// <summary>
/// Converts a short value into an enum.
/// </summary>
/// <typeparam name="T">The enum type</typeparam>
/// <param name="value">The value.</param>
/// <returns>The enum corresponding to the value. If the value is not valid for the given enum, the default value for the enum is returned.</returns>
public static T? ToEnum<T>(this short? value)
where T : struct
{
return InternalToNullableEnum<T>(value);
}
/// <summary>
/// Converts an unsigned short value into an enum.
/// </summary>
/// <typeparam name="T">The enum type</typeparam>
/// <param name="value">The value.</param>
/// <returns>The enum corresponding to the value. If the value is not valid for the given enum, the default value for the enum is returned.</returns>
[CLSCompliant(false)]
public static T? ToEnum<T>(this ushort? value)
where T : struct
{
return InternalToNullableEnum<T>(value);
}
private static T? InternalToNullableEnum<T>([CanBeNull] object value)
where T : struct
{
if (value == null)
return null;
return InternalToEnum<T>(value);
}
private static T InternalToEnum<T>([NotNull] object value)
where T : struct
{
var enumType = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
if (!enumType.GetTypeInfo().IsEnum)
throw new InvalidOperationException(enumType.Name + " is not an System.Enum.");
try
{
// ReSharper disable once PossibleNullReferenceException
return (T)Enum.ToObject(enumType, value);
}
catch (ArgumentException)
{
return default(T);
}
}
private static long ToInt64([CanBeNull] object value)
{
return Convert.ToInt64(value, CultureInfo.InvariantCulture);
}
private static void VerifyIsEnum<T>()
{
// ReSharper disable once PossibleNullReferenceException
if (!typeof(T).GetTypeInfo().IsEnum)
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Type '{0}' is not an System.Enum", typeof(T).FullName));
}
private static void VerifyIsFlagsEnum<T>()
{
VerifyIsEnum<T>();
// ReSharper disable once AssignNullToNotNullAttribute
if (!typeof(T).GetTypeInfo().GetCustomAttributes(true).OfType<FlagsAttribute>().Any())
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Type '{0}' doesn't have the 'Flags' attribute", typeof(T).FullName));
}
}
}