Say I have a function that accepts an enum decorated with the Flags attribute. If the value of the enum is a combination of more than one of the enum elements how can I extract one of those elements at random? I have the following but it seems there must be a better way.
[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();
}
You can call Enum.GetValues
to get an array of the enum's defined values, like this:
var rand = new Random();
Colors[] allValues = (Colors[])Enum.GetValues(typeof(Colors));
Colors value = allValues[rand.Next(allValues.Length)];
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)];
If you don't mind a little casting, and your enum is of underlying int type, the following will work and is fast.
var rand = new Random();
const int mask = (int)(Colours.Blue | Colours.Red | Colours.Green);
return (Colours)(mask & (rand.Next(mask) + 1));
If you only want a single flag to be set, you could do the following:
var rand = new Random();
return (Colours)(0x1 << (rand.Next(3)));
If I understand correctly, the question is about returning a random enum value from a flags enum value, not returning a random member from a flags enum.
[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;
}
Edit
And obviously you should check that the enum is a valid value, eg:
Shot myCocktail = (Shot)666;
Edit
Simplified
In my case - I have enums with missing members eg 0x01, 0x02, 0x08
, and large ulong
enum with 0x200000000
maximal value.
This code works for all of my cases:
/// <summary>
/// Gets <see cref="System.Random"/> instance.
/// </summary>
public static Random Random { get; } = new Random(Guid.NewGuid().GetHashCode());
/// <summary>
/// Gets random combination of Flags Enum.
/// </summary>
/// <typeparam name="T">Enum type.</typeparam>
/// <returns>Random Flags Enum combination.</returns>
public static T GetRandomFlagsEnumValue<T>()
where T : Enum
{
var allValues = (T[])Enum.GetValues(typeof(T));
ulong numberValue = allValues.OrderBy(x => Random.Next())
.Take(GetRandomInteger(1, allValues.Length - 1))
.Select(e => Convert.ToUInt64(e, CultureInfo.InvariantCulture))
.Aggregate((a, c) => a + c);
return (T)Enum.ToObject(typeof(T), numberValue);
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.