2011-10-16 21 views
7

Sto utilizzando un enumerato con l'attributo flags come modo di monitoraggio dello stato.Trovare il flag impostato più alto in un valore enum

Un esempio è il seguente:

Created = 1 
Completed = 2 
Dispatched = 4 

Senza scrivere qualcosa di troppo rigida (se verifica questo, fai quello, se verifica che, fare questo) Voglio essere in grado di trovare la bandiera più alta che è stata impostare quindi in questo esempio:

Item.Status = Status.Created | Status.Completed 

il metodo mitico ritornerebbe 2 - come completata è il flag impostato 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 ...?

risposta

7

Qualcosa come il seguente dovrebbe funzionare:

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

Il metodo sicuro se T non è un tipo enum, quindi un controllo dovrebbe preferibilmente essere eseguita all'inizio del metodo. Inoltre non funzionerà per un enum con un tipo sottostante più grande di int (ad esempio long).

2

Questo è il metodo di estensione che utilizzo. Essa vi darà l'enum indietro

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

uscita: maxStatus = Completato

public static class EnumExtensions { 

    /// <summary>Enumerates get flags in this collection.</summary> 
    /// 
    /// <param name="value">The value. 
    /// </param> 
    /// 
    /// <returns>An enumerator that allows foreach to be used to process get flags in this collection.</returns> 
    public static IEnumerable<T> GetFlags<T> (this T value) where T : struct { 
     return GetFlags (value, Enum.GetValues (value.GetType()).Cast<T>().ToArray()); 
    } 

    /// <summary>Enumerates get flags in this collection.</summary> 
    /// 
    /// <param name="value"> The value. 
    /// </param> 
    /// <param name="values">The values. 
    /// </param> 
    /// 
    /// <returns>An enumerator that allows foreach to be used to process get flags in this collection.</returns> 
    private static IEnumerable<T> GetFlags<T> (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<T>(); 
     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<T>(); 
     if (Convert.ToUInt64 (value) != 0L) 
      return results.Reverse<T>(); 
     if (bits == Convert.ToUInt64 (value) && values.Length > 0 && Convert.ToUInt64 (values [0]) == 0L) 
      return values.Take (1); 
     return Enumerable.Empty<T>(); 
    } 
} 
0

Come si può lanciare avanti e indietro per uint, è possibile utilizzare:

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; 
} 
Problemi correlati