2

I have a list of static variables in a static class.

namespace Test
{
  public static class Numbers
  {
    public static readonly int One = 1;
    public static readonly int Five = 5;
    public static readonly int Ten = 10;
    public static readonly int Eleven = 11;
    public static readonly int Fifteen= 15;
  }
}

And I want to randomly select a variable in the class. How can I achieve this?

int randomVariable = SomeFunction(Numbers);
Ramesh Durai
  • 2,666
  • 9
  • 32
  • 55

6 Answers6

6

Use reflection:

FieldInfo[] fields= typeof(Numbers).GetFields(
   BindingFlags.Public | BindingFlags.Static);

var rnd = new Random();
int randomVariable = (int) fields[rnd.Next(fields.Length)].GetValue(null);

Better solution without reflection:

Create an array of integers Numbers as a static property and initialize it to the values in the class Numbers:

Numbers = fields.Select(f => (int)f.GetValue()).ToArray(); //int[]

Then when getting a random value:

int randomVariable = Numbers[rnd.Next(Numbers.Length)];
Ahmed KRAIEM
  • 10,267
  • 4
  • 30
  • 33
  • +1, but don't forget you _really_ want your `Random` to be in a member or static variable rather than a local variable like this. – Rawling Jun 05 '13 at 08:12
4
  var fields = typeof(Numbers).GetFields(BindingFlags.Static | BindingFlags.Public);
  var value = fields
         .OrderBy(x => Guid.NewGuid()) // order by random
         .First() // take first field
         .GetValue(null); // get it's value

But in general it is very odd usage of static fields, I would review design, if it is everything ok with it.

Giedrius
  • 8,430
  • 6
  • 50
  • 91
3

You could create an array of int, generate a random number between the upper and lower array index and access array index with it.

Otherwise, for your approach to work, reflection must be used, which has a performance penalty and is not very elegant.. For example, get all PropertyInfos in the class, create and array with them, get and random index and invoke the PropertyInfo value.

Oscar
  • 13,594
  • 8
  • 47
  • 75
2

You can do this by using reflection on the class. Note that if you don't want to enumerate all const ints, you will need to have markers on them which to enumerate. That would be done using custom Attributes.

namespace ConsoleApplication4
{
  using System;
  using System.Collections.Generic;
  using System.Linq;

  public class Holder
  {
    public const int Number1 = 7;

    public const int Number2 = 17;

    public const int Number3 = 42;

    public static IEnumerable<int> AllNumbers()
    {
      return
        typeof(Holder).GetFields(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public)
                      .Where(p => p.FieldType == typeof(int))
                      .Select(p => (int)p.GetValue(null));
    }

    public static int RandomNumber(Random r)
    {
      var possibleNumbers = AllNumbers().ToList();

      var draw = r.Next(possibleNumbers.Count);

      return possibleNumbers[draw];
    }
  }

  public class Program
  {
    public static void Main()
    {
      var r = new Random();

      for (int i = 0; i < 10; i++)
      {
        Console.WriteLine(Holder.RandomNumber(r));
      }

      Console.WriteLine("Done");
      Console.ReadLine();
    }
  }
}
nvoigt
  • 75,013
  • 26
  • 93
  • 142
2

Since you know the type, you don't need reflection - so something as simple as this should be ok:

public sealed class NumbersRandomizer
{
    readonly Random rng = new Random();

    public int RandomValue()
    {
        switch (rng.Next(5))
        {
            case 0: return Numbers.One;
            case 1: return Numbers.Five;
            case 2: return Numbers.Ten;
            case 3: return Numbers.Eleven;
            case 4: return Numbers.Fifteen;
        }
    }
}

Which you would use like this:

var randomizer = new NumbersRandomizer();
int value = randomizer.RandomValue();
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • This solution seems OK. But the problem is, I have nearly 100 variables in the class `Numbers`. It will be difficult to write switch cases. – Ramesh Durai Jun 05 '13 at 08:25
  • 2
    @RameshDurai You don't mean "difficult" - you mean "tedious and time-consuming". ;) – Matthew Watson Jun 05 '13 at 08:27
  • So are you certain that, since you are using a 3rd party type, the 3rd party won't at some point in the future add a public readonly property that you *don't* want to include in the random numbers? Because if you use reflection, that could happen. – Matthew Watson Jun 05 '13 at 08:30
  • Nope!! If they add a new variable, that will not be a problem for me. – Ramesh Durai Jun 05 '13 at 08:32
  • 1
    @RameshDurai If that's definitely true, then the answer from nvoigt seems like it might be correct, except it's using `const` rather than `readonly` – Matthew Watson Jun 05 '13 at 08:34
  • Just to know. What was the difference between the answers of `Ahmed KRAIEM` and `nvoigt`. Because both of them are using `Random`. – Ramesh Durai Jun 05 '13 at 08:41
  • 1
    @RameshDurai Sorry, I just didn't notice which was posted first. Ahmed's was first, so that's the one to choose. :) – Matthew Watson Jun 05 '13 at 08:51
  • Very good solution for small static class like mine. Thank You. – SkyDancer Dec 11 '21 at 12:33
0

This can be achieved easier if you use an Enum.

public enum Numbers {
    One = 1,
    Five = 5,
    Ten = 10,
    Eleven = 11
    Fifteen = 15
}

Then to randomly select the enum.

var numbers = Enum.GetValues(typeof(Numbers));

Number randomNumber = (Number)numbers.GetValue(new Random().Next(numbers.Length));
Austin Brunkhorst
  • 20,704
  • 6
  • 47
  • 61