2010-06-08 2 views
5

Скажем, у меня есть функция, которая принимает перечисление, украшенное атрибутом Flags. Если значение перечисления представляет собой комбинацию из нескольких элементов перечисления, как я могу извлечь один из этих элементов случайным образом? У меня есть следующее, но кажется, что должен быть лучший способ.Случайное значение from 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(); 
} 

ответ

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

Вы можете позвонить Enum.GetValues, чтобы получить массив определенных значений перечисления, как и это:

var rand = new Random(); 

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

Я хотел бы случайное значение только подмножество перечисления, как определяется побитовая комбинация, как «Синий | Красный». Извините за неясность. –

1

Если я правильно понимаю, речь идет о возврате случайного значения перечисления от А флагов перечисления значений , не возвращая случайный элемент из перечисления флагов.

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

Редактировать

И, очевидно, вы должны проверить, что перечисление является допустимым значением, например .:

Shot myCocktail = (Shot)666; 

Редактировать

упрощенный

+1

Вы указали правильную формулировку проблемы. Однако я думаю, что ваша случайная функция не вернет значения с равномерным распределением. При вероятности 50/50 для каждого значения через цикл последующие значения имеют меньшую вероятность выбора? –

+0

Я вижу вашу точку зрения. Строго говоря, он все еще случайный и отвечает на ваш вопрос. Я обновил его до одного дистрибутива. Уже поздно, и я уверен, что я, наверное, набился! :) –

+0

И теперь упрощено. –

2

I f вы не возражаете против небольшого кастинга, а ваше перечисление имеет базовый тип int, следующее будет работать и быстро.

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

Если вы хотите только один флаг должен быть установлен, вы можете сделать следующее:

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

Если результат «(int) allValues ​​& rand.Next()» равен нулю, и у вас нет нуля enum member, вы получите недопустимое перечисление, то есть (Цвета) 0. –

+0

Правда. Позвольте мне посмотреть, если это можно исправить ... – bbudge

+0

Хорошо, я думаю, что это работает сейчас. Но он стал немного грязнее и менее ясен. Только полезно, если производительность является серьезной проблемой. – bbudge

Смежные вопросы