2010-06-08 20 views
5

Digamos que tengo una función que acepta una enumeración decorada con el atributo Flags. Si el valor de la enumeración es una combinación de más de uno de los elementos enum ¿cómo puedo extraer uno de esos elementos al azar? Tengo lo siguiente, pero parece que debe haber una mejor manera.Valor aleatorio de Flags enum

[Flags] 
enum Colours 
{ 
    Blue = 1, 
    Red = 2, 
    Green = 4 
} 

public static void Main() 
{ 
    var options = Colours.Blue | Colours.Red | Colours.Green; 
    var opts = options.ToString().Split(','); 
    var rand = new Random(); 
    var selected = opts[rand.Next(opts.Length)].Trim(); 
    var myEnum = Enum.Parse(typeof(Colours), selected); 
    Console.WriteLine(myEnum); 
    Console.ReadLine(); 
} 

Respuesta

8
var options = Colours.Blue | Colours.Green; 

var matching = Enum.GetValues(typeof(Colours)) 
        .Cast<Colours>() 
        .Where(c => (options & c) == c) // or use HasFlag in .NET4 
        .ToArray(); 

var myEnum = matching[new Random().Next(matching.Length)]; 
9

Puede llamar Enum.GetValues para obtener una matriz de valores definidos de la enumeración, así:

var rand = new Random(); 

Colors[] allValues = (Colors[])Enum.GetValues(typeof(Colors)); 
Colors value = allValues[rand.Next(allValues.Length)]; 
+0

Me gustaría tener un valor aleatorio de sólo un subconjunto de la enumeración como se define por una combinación bit a bit como "Azul | Rojo". Perdón por no ser más claro. –

1

Si he entendido bien, la pregunta es acerca de regresar un valor de enumeración azar de un valor de enumeración banderas , no devuelve un miembro aleatorio de una enumeración de banderas.

[Flags] 
    private enum Shot 
    { 
     Whisky = 1, 
     Absynthe = 2, 
     Pochin = 4, 
     BrainEraser = Whisky | Absynthe | Pochin 
    } 

    [Test] 
    public void Test() 
    { 
     Shot myCocktail = Shot.Absynthe | Shot.Whisky; 

     Shot randomShotInCocktail = GetRandomShotFromCocktail(myCocktail); 
    } 

    private static Shot GetRandomShotFromCocktail(Shot cocktail) 
    { 
     Random random = new Random(); 

     Shot[] cocktailShots = Enum.GetValues(typeof(Shot)). 
      Cast<Shot>(). 
      Where(x => cocktail.HasFlag(x)).ToArray(); 

     Shot randomShot = cocktailShots[random.Next(0, cocktailShots.Length)]; 

     return randomShot; 
    } 

Editar

Y, obviamente, debe comprobar que la enumeración es un valor válido, por ejemplo .:

Shot myCocktail = (Shot)666; 

Editar

simplificado

+1

Tiene razón al declarar el problema. Sin embargo, creo que su función aleatoria no devolverá los valores con una distribución pareja. Con una probabilidad del 50/50 para cada valor a través del bucle, los valores subsiguientes tienen una menor posibilidad de ser seleccionados. –

+0

Veo tu punto. Estrictamente hablando, sigue siendo aleatorio y responde su pregunta. Lo he actualizado a una distribución par. ¡Es tarde y estoy seguro de que probablemente me he llenado! :) –

+0

Y ahora simplificado. –

2

I Si no le molesta un pequeño casting, y su enumeración es del tipo int subyacente, lo siguiente funcionará y es rápido.

var rand = new Random(); 
const int mask = (int)(Colours.Blue | Colours.Red | Colours.Green); 
return (Colours)(mask & (rand.Next(mask) + 1)); 

Si sólo desea una sola bandera para ajustar, se puede hacer lo siguiente:

var rand = new Random(); 
return (Colours)(0x1 << (rand.Next(3))); 
+0

Si el resultado de "(int) allValues ​​& rand.Next()" es cero, y no tiene cero miembro enum, obtienes una enumeración no válida, es decir (Colores) 0. –

+0

Es cierto. Déjame ver si se puede arreglar ... – bbudge

+0

Bien, creo que funciona ahora. Pero se volvió un poco más desordenado y menos claro. Solo es útil si el rendimiento es una preocupación seria. – bbudge