27

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?

Cole Tobin
  • 9,206
  • 15
  • 49
  • 74

6 Answers6

33

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.

Cole Tobin
  • 9,206
  • 15
  • 49
  • 74
BrokenGlass
  • 158,293
  • 28
  • 286
  • 335
  • 1
    And in some apps being repeatable is not necessary. I can think of situations where you'd want to seed it manually, but I've never personally needed to explicitly set one. – Davy8 Feb 08 '11 at 13:53
  • 1
    @Davy8 - that's very true - in almost all my apps I do not need a seed either, but `Random` still covers that functionality. – BrokenGlass Feb 08 '11 at 13:56
  • I have a program of Racing dogs, which move in random and one will won. Is the solution to make same seed?? –  Feb 08 '11 at 14:19
  • 1
    Getting repeatable random numbers is pretty important for troubleshooting. Algorithms like simulated annealing and Monte Carlo simulation only need the random number sequence to be random, they don't care about the actual values. – Hans Passant Feb 08 '11 at 14:23
  • AutoPoco uses set random seeds to very good effect to make known random values. Very useful in unit testing. – Rangoric Feb 08 '11 at 14:24
  • @Mr.DDD: I'm pretty sure in that scenario you will always want the default constructor which takes the current time as seed - that ensures you that no two races are alike (assuming you don't re-create the `Random` instance in very short order). I would just keep an instance member of type `Random` in your class and re-use it for every race (again assuming the app is single-threaded, see @Jon Skeet's point about Random not being thread-safe) – BrokenGlass Feb 08 '11 at 14:28
  • @BrokenGlass : How to use current time as seed ? is it this: Radom rnd = new Random(Convert.ToInt16(DateTime.Now.Ticks)); , thanks. –  Feb 08 '11 at 14:33
  • 1
    @Mr.DDD: It's the default constructor (the rest is done by the `Random` class for you): `Random rnd = new Random();` – BrokenGlass Feb 08 '11 at 14:35
  • @BrokenGlass why random class generate same value if it isn't statci? – AminM Jul 20 '21 at 04:39
18

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.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    +1 for thread safety. I sometimes mistakenly make the assumption that built-in classes are thread-safe unless marked otherwise when they're actually not thread-safe unless they're stated to be so. – Davy8 Feb 08 '11 at 13:59
4

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

Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
2

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; }
}
1

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.

supercat
  • 77,689
  • 9
  • 166
  • 211
1

Why? It was a design mistake.

This late-1990s design it not an easy thing to use correctly. It pushes developers into the of failure. It is amazing how many developers are forced to invent their own horrific solutions:

new Random().Next(1, 100);

Not even counting the mental energy needed on the part of every new developer to needing a random number in C#.

The design was well-meaning at the time, using logical arguments why it shouldn't have a static method (you can implement the static class easily yourself using our provided building blocks). The problem is that the design leads itself to excessive memory use, as well as poor security for those who needed to depend on randomness.

Those developers who truly needed a specifically seeded RNG for testing: they could have instantiated the class, and build the functionality using the building blocks provided by the .NET team.

What's odd today is that some will take the design itself as the gospel only way to do it, then go on to justify the design when instead they're justifying our code has to be the way it has to be.

This design would not have survived the late 2000s design review process that went into WinRT - where they focused on the "pit of success". The team would have recognized the fundamental design flaws in the API as it is, and fixed it. Which is what they did - and that WinRT API is easy to call correctly:

Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219