List all bit names from a flag Enum

早过忘川 提交于 2019-11-28 10:00:37
Gabe

Here's a simple way to write it using LINQ:

public static IEnumerable<T> MaskToList<T>(Enum mask)
{
    if (typeof(T).IsSubclassOf(typeof(Enum)) == false)
        throw new ArgumentException();

    return Enum.GetValues(typeof(T))
                         .Cast<Enum>()
                         .Where(m => mask.HasFlag(m))
                         .Cast<T>();
}

If your desired end result is a string list of names, just call mask.ToString().

What would you do if the enum were defined like this:

[Flags]
enum State
{
    Ready = 1,
    Waiting = 2,
    ReadyAndWaiting = 3
}

As to resolving the compiler error, this should do it:

Enum bit = (Enum)(object)curValueBit;

Jon Skeet has a project called unconstrained melody that allows you to add the enum constraint, after compilation, by rewriting the IL. This works because the CLR supports such a constraint, even though C# does not.

Another thought: It will be more efficient to cast the return value of GetValues directly to T[]:

foreach(T curValueBit in (T[])Enum.GetValues(typeof (T)))
Andrei Rînea

Building on Gabe's answer I came up with this :

public static class EnumHelper<T>
    where T : struct
{
    // ReSharper disable StaticFieldInGenericType
    private static readonly Enum[] Values;
    // ReSharper restore StaticFieldInGenericType
    private static readonly T DefaultValue;

    static EnumHelper()
    {
        var type = typeof(T);
        if (type.IsSubclassOf(typeof(Enum)) == false)
        {
            throw new ArgumentException();
        }
        Values = Enum.GetValues(type).Cast<Enum>().ToArray();
        DefaultValue = default(T);
    }

    public static T[] MaskToList(Enum mask, bool ignoreDefault = true)
    {
        var q = Values.Where(mask.HasFlag);
        if (ignoreDefault)
        {
            q = q.Where(v => !v.Equals(DefaultValue));
        }
        return q.Cast<T>().ToArray();
    }
}

I organized things a bit differently, namely I put the type check (i.e.: the verification that T is really an enumeration) and the obtaining of the enum values in the static constructor so this is done only once (this would be a performance improvement).

Another thing, I added an optional parameter so you can ignore the typical "zero" / "None" / "NotApplicable" / "Undefined" / etc value of the enumeration.

What if just do something like this:

public static IEnumerable<T> MaskToList<T>(Enum mask)
{
 if (typeof(T).IsSubclassOf(typeof(Enum)) == false)
    throw new ArgumentException();

  List<T> toreturn = new List<T>(100);

  foreach(T curValueBit in Enum.GetValues(typeof (T)).Cast<T>())
  {
    Enum bit = (curValueBit as Enum);  // The only difference is actually here, 
                                       //  use "as", instead of (Enum) cast

    if (mask.HasFlag(bit))
      toreturn.Add(curValueBit);
  }

  return toreturn;
}

As the as has not compile time check. Compiler here just "believes" you, hoping that you know what you're doing, so the compile time error not raised.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!