Writing a generic utility to mask together all the values in a c# enum turns out to be much more difficult than one would imagine, because
- The underlying type of an enum can be byte, short, int or long; signed or unsigned.
- There is no way to cast an object directly to a generic enum, perhaps because there's no enum constraint built into c#. One must box instead, then unbox.
- All the enum utilities date from c# 1.0 and so are rather crufty.
This is the best I could do. I made use of the following:
- Cast Int to Generic Enum in C#
- Enum type constraints in C#
Would this be easier in c++/CLI?
/// <summary>
/// Contains generic utilities for enums, constrained for enums only.
/// </summary>
public sealed class EnumHelper : Enums<Enum>
{
private EnumHelper()
{
}
}
/// <summary>
/// For use by EnumHelper, not for direct use.
/// </summary>
public abstract class Enums<TEnumBase> where TEnumBase : class, IConvertible
{
private static void ThrowOnEnumWithoutFlags<TEnum>() where TEnum : struct, TEnumBase
{
var attributes = typeof(TEnum).GetCustomAttributes(typeof(FlagsAttribute), false);
if (attributes.Length == 0)
{
throw (new ArgumentException("The generic argument [<T>] must be an enumeration with the [FlagsAttribute] applied.", "T: " + typeof(TEnum).FullName));
}
}
public static TEnum GetAll<TEnum>() where TEnum : struct, TEnumBase
{
ThrowOnEnumWithoutFlags<TEnum>();
var underlyingType = Enum.GetUnderlyingType(typeof(TEnum));
if (underlyingType == typeof(ulong))
{
ulong value = 0;
foreach (var v in Enum.GetValues(typeof(TEnum)))
// Not sure I need the culture but Microsoft passes it in Enum.ToUInt64(Object value) - http://referencesource.microsoft.com/#mscorlib/system/enum.cs
value |= Convert.ToUInt64(v, CultureInfo.InvariantCulture);
return (TEnum)Enum.ToObject(typeof(TEnum), value);
}
else
{
long value = 0;
foreach (var v in Enum.GetValues(typeof(TEnum)))
// Not sure I need the culture but Microsoft passes it in Enum.ToUInt64(Object value) - http://referencesource.microsoft.com/#mscorlib/system/enum.cs
value |= Convert.ToInt64(v, CultureInfo.InvariantCulture);
return (TEnum)Enum.ToObject(typeof(TEnum), value);
}
}
I have tested on byte, sbyte, short, int, long, ushort, uint and ulong types.
Update simplified as per hvd's suggestion.