Генерация случайного перечисления в С# 2.0

Может ли кто-нибудь указать мне более чистый метод для создания случайного члена перечисления. Это работает, но выглядит некрасиво.

Спасибо!

public T RandomEnum<T>()
{
  string[] items = Enum.GetNames(typeof( T ));
  Random r = new Random();
  string e = items[r.Next(0, items.Length - 1)];
  return (T)Enum.Parse(typeof (T), e, true);
}

person user10178    schedule 26.11.2008    source источник


Ответы (5)


public T RandomEnum<T>()
{ 
  T[] values = (T[]) Enum.GetValues(typeof(T));
  return values[new Random().Next(0,values.Length)];
}

Спасибо @[Marc Gravell] за указание на то, что максимальное значение в Random.Next(min,max) является эксклюзивным.

person Mark Cidade    schedule 26.11.2008
comment
Конечно, в реальной реализации вы не захотите использовать новый Random каждый вызов (особенно если вы его часто вызываете), и лично я хотел бы кэшировать массив. - person Dolphin; 16.12.2009
comment
Обратите внимание, что в Silverlight нет Enum.GetValues. См. альтернативное решение ниже. - person Frode Lillerud; 04.01.2011
comment
ОСТЕРЕГАЙТЕСЬ Этот ответ ошибочен. Как было сказано ранее, если генерация сплочена, случайности не будет вообще. - person Luis; 08.11.2012

Ответ Маркидада хорош (обратите внимание, что вам нужно только Next(0,values.Length), поскольку верхняя граница является эксклюзивной), но следите за временем. Если вы сделаете это в узком цикле, вы получите много повторений. Чтобы сделать его более случайным, рассмотрите возможность сохранения объекта Random в поле, т.е.

private Random rand = new Random();
public T RandomEnum<T>()
{ 
  T[] values = (T[]) Enum.GetValues(typeof(T));
  return values[rand.Next(0,values.Length)];
}

Если это статическое поле, вам потребуется синхронизировать доступ.

person Marc Gravell    schedule 26.11.2008
comment
В моем случае это просто генерация некоторых значений по умолчанию для игры — никаких циклов. Спасибо за совет! - person user10178; 26.11.2008

В Silverlight нет GetValues(), но вместо этого вы можете использовать отражение для получения случайного перечисления.

private Random rnd = new Random();

public T RndEnum<T>()
{
    FieldInfo[] fields = typeof(T).GetFields(BindingFlags.Static | BindingFlags.Public);

    int index = rnd.Next(fields.Length);

    return (T) Enum.Parse(typeof(T), fields[index].Name, false);
}
person pixie    schedule 15.12.2009
comment
Спасибо, мне нужно это для Silverlight! - person Frode Lillerud; 04.01.2011

Я не уверен насчет С#, но другие языки допускают пробелы в значениях перечисления. Чтобы учесть это:

enum A {b=0,c=2,d=3,e=42};

switch(rand.Next(0,4))
{
   case 0: return A.b;
   case 1: return A.c;
   case 2: return A.d;
   case 3: return A.e;
}

Главный недостаток - поддерживать его в актуальном состоянии!

Не так аккуратно, но более правильно в этом угловом случае.


Как уже отмечалось, приведенные выше примеры индексируются в массив допустимых значений, и это правильно. OTOH, некоторые языки (cough D cough) не предоставляют этот массив, поэтому вышеприведенное достаточно полезно, поэтому я все равно оставлю его.

person BCS    schedule 26.11.2008
comment
marxidad учитывает это, возвращая индекс массива, а не значение перечисления. Я сделал то же самое, я просто выбрал живописный маршрут! - person user10178; 26.11.2008
comment
Это могло бы быть яснее. Перечисление генерируется по его положению в массиве, а не по его значению. - person user10178; 26.11.2008

person    schedule
comment
Этот метод не работает, если между числовыми значениями Enum Values ​​есть промежутки (и это вполне возможно). - person Styxxy; 28.10.2012