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 :
Method | Mean | Error | StdDev |
---|---|---|---|
RandomRecreate | 1,631.14 ns | 8.217 ns | 7.284 ns |
RandomReuse | 12.02 ns | 0.001 ns | 0.001 ns |
RandomGenerator | 74.81 ns | 0.473 ns | 0.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.
Loved this article. We can extend it further to include information about RNGCryptoServiceProvider which is RNG over CSP. Hope it resonates with you!
Great article! I would have a question! in my Asp Net Core MVC / .Net6 / project.
I have a simple property public int RandomNumber { get; set; } and I’m using it in Only in the Create /HttpPost / method, The goal is once is created this RandomNumber and it goes to Sql Server it won’t repeat it.
It seems like works but how do I make sure it won’t repeat?
Here is my Controller:
You might want to check our SQL Server Sequence values. While they are sequenced and not random, it does mean that there will never be a duplicate batch number. https://dotnetcoretutorials.com/2022/03/22/using-sql-server-sequences-to-generate-unique-values/