Jak wybrać losową wartość z wyliczenia?

171

Biorąc pod uwagę arbitralne wyliczenie w C #, w jaki sposób mogę wybrać wartość losową?

(Nie znalazłem tego bardzo podstawowego pytania w SO. Za chwilę opublikuję swoją odpowiedź jako punkt odniesienia dla każdego, ale możesz napisać własną odpowiedź.)

mafu
źródło

Odpowiedzi:

282
Array values = Enum.GetValues(typeof(Bar));
Random random = new Random();
Bar randomBar = (Bar)values.GetValue(random.Next(values.Length));
Darin Dimitrov
źródło
40
Upewnij się jednak, że nie odtwarzasz randomw ciasnej pętli - w przeciwnym razie uzyskasz tę samą wartość.
ChrisF
1
Czy powinno to być losowe.Next (values.Length -1)?
uriDium
7
@uriDium Nie, argument określa, która wartość jest pierwsza za duża do zwrócenia (tj. max minus 1 )
mafu
wartości Długość - 1
Bojidar Stanchev
DarinDimitrov IMO pierwszy komentarz @ChrisF powinien znajdować się w odpowiedzi z uznaniem dla Chrisa.
maytham-ɯɐɥʇʎɐɯ
62

Użyj Enum.GetValues, aby pobrać tablicę wszystkich wartości. Następnie wybierz losowy element tablicy.

static Random _R = new Random ();
static T RandomEnumValue<T> ()
{
    var v = Enum.GetValues (typeof (T));
    return (T) v.GetValue (_R.Next(v.Length));
}

Test:

for (int i = 0; i < 10; i++) {
    var value = RandomEnumValue<System.DayOfWeek> ();
    Console.WriteLine (value.ToString ());
}

->

Tuesday
Saturday
Wednesday
Monday
Friday
Saturday
Saturday
Saturday
Friday
Wednesday
mafu
źródło
5

Możesz po prostu zrobić to:

var rnd = new Random();
return (MyEnum) rnd.Next(Enum.GetNames(typeof(MyEnum)).Length);

Nie ma potrzeby przechowywania tablic

Breno Angelotti
źródło
GetNameszwraca tablicę.
Nathan Tuggy
Miałem na myśli, że nie musisz go przechowywać. Moja wina
Breno Angelotti
Jeśli ktoś robi to w pętli, za każdym razem wywołuje GetNames zamiast buforować go w tablicy. Spowolniłoby to ich kod, więc nie widzę, jaki jest twój wkład tutaj?
Bojidar Stanchev
@BojidarStanchev IF , w moim przypadku to działa wspaniale, dziękuję Breno :)
Jaacko Torus
4

Oto alternatywna wersja jako Extension Methodusing LINQ.

using System;
using System.Linq;

public static class EnumExtensions
{
    public static Enum GetRandomEnumValue(this Type t)
    {
        return Enum.GetValues(t)          // get values from Type provided
            .OfType<Enum>()               // casts to Enum
            .OrderBy(e => Guid.NewGuid()) // mess with order of results
            .FirstOrDefault();            // take first item in result
    }
}

public static class Program
{
    public enum SomeEnum
    {
        One = 1,
        Two = 2,
        Three = 3,
        Four = 4
    }

    public static void Main()
    {
        for(int i=0; i < 10; i++)
        {
            Console.WriteLine(typeof(SomeEnum).GetRandomEnumValue());
        }
    }           
}

Dwa
Jeden
Cztery
Cztery
Cztery
Trzy
Dwa
Cztery
Jeden
Trzy

Aaron Hudon
źródło
2

Zadzwoń Enum.GetValues; zwraca to tablicę, która reprezentuje wszystkie możliwe wartości wyliczenia. Wybierz losowy element z tej tablicy. Przerzuć ten element z powrotem do oryginalnego typu wyliczenia.

Tim Robinson
źródło
2

Oto jego ogólna funkcja. Utrzymuj tworzenie RNG poza kodem wysokiej częstotliwości.

public static Random RNG = new Random();

public static T RandomEnum<T>()
{  
    Type type = typeof(T);
    Array values = Enum.GetValues(type);
    lock(RNG)
    {
        object value= values.GetValue(RNG.Next(values.Length));
        return (T)Convert.ChangeType(value, type);
    }
}

Przykład użycia:

System.Windows.Forms.Keys randomKey = RandomEnum<System.Windows.Forms.Keys>();
CAŁOŚĆ
źródło
Posiadanie metody statycznej, która nie jest bezpieczna dla wątków, jest dość niebezpieczne.
CodesInChaos
@CodesInChaos Masz rację. Random.Next () nie jest bezpieczna wątkowo i zacznie zwracać zera po zerwaniu. Zaktualizowałem moją odpowiedź na podstawie tych informacji.
CAŁY
1

Osobiście jestem fanem metod rozszerzających, więc użyłbym czegoś takiego (chociaż nie jest to rozszerzenie, wygląda podobnie):

public enum Options {
    Zero,
    One,
    Two,
    Three,
    Four,
    Five
}

public static class RandomEnum {
    private static Random _Random = new Random(Environment.TickCount);

    public static T Of<T>() {
        if (!typeof(T).IsEnum)
            throw new InvalidOperationException("Must use Enum type");

        Array enumValues = Enum.GetValues(typeof(T));
        return (T)enumValues.GetValue(_Random.Next(enumValues.Length));
    }
}

[TestClass]
public class RandomTests {
    [TestMethod]
    public void TestMethod1() {
        Options option;
        for (int i = 0; i < 10; ++i) {
            option = RandomEnum.Of<Options>();
            Console.WriteLine(option);
        }
    }

}
Dan Champagne
źródło
1
Począwszy od C # 7.3, możesz ograniczyć typ ogólny do wyliczenia: public static T Of<T>() where T : Enum docs.microsoft.com/en-us/visualstudio/releasenotes/ ...
nitzel
0

Dostosowane jako rozszerzenie klasy Random:

public static class RandomExtensions
{   
    public static T NextEnum<T>(this Random random)
    {
        var values = Enum.GetValues(typeof(T));
        return (T)values.GetValue(random.Next(values.Length));
    }
}

Przykład użycia:

var random = new Random();
var myEnumRandom = random.NextEnum<MyEnum>();
borja garcia
źródło
0

Możesz także rzucić losową wartość:

using System;

enum Test {
  Value1,
  Value2,
  Value3
}

class Program {
  public static void Main (string[] args) {
    var max = Enum.GetValues(typeof(Test)).Length;
    var value = (Test)new Random().Next(0, max - 1);
    Console.WriteLine(value);
  }
}

Ale powinieneś użyć lepszego randomizera, takiego jak ten w mojej bibliotece .

gsscoder
źródło