List all bit names from a flag Enum

后端 未结 4 594
伪装坚强ぢ
伪装坚强ぢ 2020-12-10 04:26

I\'m trying to make a helper method for listing the names of all bits set in an Enum value (for logging purposes). I want have a method that would return the list of all the

相关标签:
4条回答
  • 2020-12-10 04:31

    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.

    0 讨论(0)
  • 2020-12-10 04:43

    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>();
    }
    
    0 讨论(0)
  • 2020-12-10 04:49

    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)))
    
    0 讨论(0)
  • 2020-12-10 04:51

    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.

    0 讨论(0)
提交回复
热议问题