2011-10-16 22 views
7

Estoy usando un atributo de enumeración con indicadores como una forma de seguimiento del estado.Encontrar el indicador de conjunto más alto en un valor de enumeración

Un ejemplo es el siguiente:

Created = 1 
Completed = 2 
Dispatched = 4 

Sin escribir nada demasiado rígido (si comprobación de esto, haz aquello, si comprobación de que, hacer esto) quiero ser capaz de encontrar la bandera más alta que ha sido establecer por lo que en este ejemplo:

Item.Status = Status.Created | Status.Completed 

el método mítico volvería 2 - como completada es la bandera de conjunto con el valor más alto.

GetMaxSetFlagValue(Item.Status) // returns 2 

He encontrado preguntas que giran en torno a la enumeración real, simplemente no es un valor que usa banderas. Estoy bastante seguro de que esto podría lograrse con Linq ...?

Respuesta

7

algo como el siguiente debería funcionar:

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

El método fallará si T no es un tipo de enumeración, por lo que una comprobación debe realizarse preferiblemente en el principio del método. Tampoco funcionará para una enumeración con un tipo subyacente mayor que int (es decir, long).

2

Este es el método de extensión que uso. Se le dará la enumeración de vuelta

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

Salida: maxStatus = Completado

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

Como se puede echar hacia atrás y adelante a uint, que puede usar:

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; 
} 
Cuestiones relacionadas