When you use the System.Random
class, you must make an instance of it. Why is it not static
? Because if I want a random number between 0 and 9, I can use the static method, System.Random.Next(int, int)
:
int ourRandomNumber = Random.Next(0,9);
So why isn't the class just static
?
You wouldn't be able to use different seeds if it were static - the Random instance keeps track of that state.
By default Random uses the current time as seed, but re-using a particular seed (i.e. new Random(42)
) allows you to exactly repeat the sequence of random numbers - they will always be the same for the same seed. This aspect is very important in some applications. For example, Minecraft.
Random
is not thread-safe. It's fine to have one instance of Random
per thread, but you shouldn't use one instance from multiple threads concurrently. So you can't just have one instance of Random
in a static variable and use that from the static method.
Also, making it static would remove the opportunity to give a specific seed, as mentioned by BrokenGlass
.
Of course, it wouldn't be too hard to create static methods which took care of the thread safety when you don't need to specify a seed, but still leave the instance methods for when you want to use a particular instance. Personally I find it appropriate to treat "a source of random numbers" as a dependency to be injected where appropriate.
I have an article which covers some of this and which you may find useful.
Sometimes you want "something random", and you don't care about how that random value is arrived at. Having a static method for that could work.
However, sometimes you want to be able to repeatbly obtain the same random sequence. For that, you use the overload of the constructor that takes a seed value, and in that case, you don't want any other code that's using random numbers to consume one of the numbers from your sequence. In that case, you definitely need an instance of the class
Having a repeatable 'random' sequence is useful in testing scenarios.
For example, you could use it in testing a game engine to ensure that an AI was correctly picking targets, or paths - even if it has a random path evaluation.
Here is a very simplistic example. No matter how many times you run this test, it will always pick the same three cards when given the same base random number generator.
This can be useful to ensure that the random number generator being used is the one supplied. And, for some reason if a new random number generator were introduced without altering the test, then the test would fail.
[TestMethod]
public void TestRandomPicking()
{
Random random = new Random(1);
Deck deck = new Deck(random);
Assert.AreEqual(3, deck.PickCard().Value);
Assert.AreEqual(1, deck.PickCard().Value);
Assert.AreEqual(5, deck.PickCard().Value);
}
public class Deck
{
public Deck()
{
_randomizer = new Random();
}
public Deck(Random randomizer)
{
_randomizer = randomizer;
}
Random _randomizer;
private List<Card> _cards = new List<Card>
{
new Card {Value = 1},
new Card {Value = 2},
new Card {Value = 3},
new Card {Value = 4},
new Card {Value = 5},
new Card {Value = 6},
new Card {Value = 7},
new Card {Value = 8},
new Card {Value = 9},
new Card {Value = 10}
};
private List<Card> Cards { get { return _cards; } }
public Card PickCard()
{
return Cards[_randomizer.Next(0, Cards.Count - 1)];
}
}
public class Card
{
public int Value { get; set; }
}
Often when one is debugging a program, improper behavior at one step may not have visible symptoms until many more steps have executed, by which time the original cause may have been obscured. In such cases, it can be very useful to be able to restart from scratch a program which malfunctioned on e.g. step 1,000,000 and have it run the first 999,990 or so steps exactly as it did the first time and then pause to let the programmer examine its state. Such debugging won't be possible if a program generates truly "random" numbers, but will be if it instead uses a pseudo-random generator which can be reloaded on the second run with the same seed as was used on the first run.