Generating Random Numbers In C# .NET

True randomness has always been something I struggled to get my head around. Even how we might get closer to being “true random” is also slightly befuddling to me. And that’s why when I was recently asked “What’s the best way to generate a random number in .NET”, a shiver went down my spine remembering the chat boards back in the day flaming people because “The Random class in .NET is not truly random!!”.

What does it mean to be truly random? Or atleast close to it? In practical terms, it means that if we generated a billion numbers in a row, there would be no predictability or uniform to them. They wouldn’t bias in any particular direction overall, but they also wouldn’t be predictable (even within a range) on what the next number would be.

So how do we achieve this in .NET?

The Random Class

For a long time now, we’ve been able to use the Random class to generate “random” numbers. For example :

var randomGenerator = new Random();
randomGenerator.Next(1, 1000000);

This generates us a random number between 1 and 1 million.

However, the Random in C# uses a “seed” value that then uses an algorithm to generator numbers from that seed. Given the same seed value, you would end up with the same number. For example :

var randomGenerator = new Random(123);
var random1 = randomGenerator.Next(1, 1000000);

randomGenerator = new Random(123);
var random2 = randomGenerator.Next(1, 1000000);

Console.WriteLine(random1 + " - " + random2); //Will output the same number. 

And so you probably say, well then just don’t give it the same seed? Easy enough right? Well actually it always has a seed whether you like it or not. When the Random constructor is not given a seed, in .NET Framework it uses the millisecond portion of the time as a seed, and in .NET Core/.NET 5+, it uses a pseudo random number generator.

What this means in .NET Framework according to the documentation :

On most Windows systems, Random objects created within 15 milliseconds of one another are likely to have identical seed values

But this only occurs if you construct your random generator multiple times, if you instead use the same instance of random each time, then this type of “collision” won’t occur :

var randomGenerator = new Random();
var random1 = randomGenerator.Next(1, 1000000);
var random2 = randomGenerator.Next(1, 1000000); //Different number because we are using the same Random instance

I may have been waffling here. But the point is that the Random class in .NET is “good” but not “truly random”. If you are using the same instance of Random in your code to generate multiple random numbers, chances are you will be fine, but it’s still not ideal.

So what can we use if you want more randomness?

Using The RandomNumberGenerator Class

Instead of using the Random class, you can use the RandomNumberGenerator class… Which admittedly is a little annoying to have two. But let’s take a look!

This class has actually been around in all versions of .NET, but in .NET Core 3+,  it got some love with additional helper methods added. So instead of working with random bytes, you can get your random numbers handed right back to you.

var random = RandomNumberGenerator.GetInt32(1, 1000000);

This random number generator is built ontop of the cryptography API’s to be as truly random as possible. I could go on about this (And probably get it all wrong), but if you need something secured by randomness (For example password salt), then you should be using the RandomNumberGenerator and not the Random class.

Performance

So that probably leaves you thinking, why even bother ever using the Random class? Surely if I can get *more* randomness from the RandomNumberGenerator, I should always just use that. Well, it does come at a cost.

Using the following benchmark setup :

public class RandomBenchmark
{
    private static Random _random = new Random();

    [Benchmark]
    public int RandomRecreate() => new Random().Next(1, 1000000);

    [Benchmark]
    public int RandomReuse() => _random.Next(1, 1000000);

    [Benchmark]
    public int RandomGenerator() => RandomNumberGenerator.GetInt32(1, 1000000);
}

We get the following results :

MethodMeanErrorStdDev
RandomRecreate1,631.14 ns8.217 ns7.284 ns
RandomReuse12.02 ns0.001 ns0.001 ns
RandomGenerator74.81 ns0.473 ns0.395 ns

I’ve included the “Recreate” benchmark here just to show how expensive it is to new up a new Random class (Even though, as I’ve already explained, you do not want to do so). But what we are really looking at is RandomReuse vs RandomGenerator. Between the two, using RandomGenerator is 6x slower than using the Random class. We are still talking about nanoseconds between the two so for most use cases, it’s not going to make a difference, but it is something to be aware of.

ENJOY THIS POST?
Join over 3.000 subscribers who are receiving our weekly post digest, a roundup of this weeks blog posts.
We hate spam. Your email address will not be sold or shared with anyone else.

Leave a Reply

Your email address will not be published. Required fields are marked *