Trovare il flag set più alto in un valore enum

Sto usando un enum con l’attributo flags come modo per monitorare lo stato.

Un esempio è il seguente:

Created = 1 Completed = 2 Dispatched = 4 

Senza scrivere nulla di troppo rigido (se lo controlli, fallo, se lo controlli, fai questo) voglio essere in grado di trovare il flag più alto che è stato impostato così in questo esempio:

 Item.Status = Status.Created | Status.Completed 

il metodo mitico restituirebbe 2 – come completato è la bandiera impostata con il valore più alto.

 GetMaxSetFlagValue(Item.Status) // returns 2 

Ho trovato domande che ruotavano attorno all’enumerazione attuale, ma non un valore che utilizza le bandiere. Sono abbastanza sicuro che questo potrebbe essere raggiunto con Linq …?

Qualcosa come il seguente dovrebbe funzionare:

 static int GetMaxSetFlagValue(T flags) where T : struct { int value = (int)Convert.ChangeType(flags, typeof(int)); IEnumerable setValues = Enum.GetValues(flags.GetType()).Cast().Where(f => (f & value) == f); return setValues.Any() ? setValues.Max() : 0; } 

Il metodo fallirà se T non è un tipo enum, quindi dovrebbe essere preferibilmente eseguito un controllo all’inizio del metodo. Inoltre non funzionerà per un enum con un tipo sottostante più grande di int (cioè long ).

Questo è il metodo di estensione che utilizzo. Ti restituirà l’enume

 var maxStatus = Item.Status.GetFlags().Max(); 

Uscita: maxStatus = Completato

 public static class EnumExtensions { /// Enumerates get flags in this collection. /// /// The value. ///  /// /// An enumerator that allows foreach to be used to process get flags in this collection. public static IEnumerable GetFlags (this T value) where T : struct { return GetFlags (value, Enum.GetValues (value.GetType ()).Cast ().ToArray ()); } /// Enumerates get flags in this collection. /// ///  The value. ///  /// The values. ///  /// /// An enumerator that allows foreach to be used to process get flags in this collection. private static IEnumerable GetFlags (T value, T [] values) where T : struct { if (!typeof (T).IsEnum) { throw new ArgumentException ("Type must be an enum."); } ulong bits = Convert.ToUInt64 (value); var results = new List (); for (int i = values.Length - 1; i >= 0; i--) { ulong mask = Convert.ToUInt64 (values [i]); if (i == 0 && mask == 0L) break; if ((bits & mask) == mask) { results.Add (values [i]); bits -= mask; } } if (bits != 0L) return Enumerable.Empty (); if (Convert.ToUInt64 (value) != 0L) return results.Reverse (); if (bits == Convert.ToUInt64 (value) && values.Length > 0 && Convert.ToUInt64 (values [0]) == 0L) return values.Take (1); return Enumerable.Empty (); } } 

Come puoi lanciare avanti e indietro per uint, puoi usare:

 public uint LowestBit(uint x) { return ~(x&x-1)&x; } public uint HighestBit(uint x) { uint last = x; while (x!=0) { last=x; x&=x-1; } return last; }