How to Generate Random Numbers and Strings in C#

August 12, 2011 by Vlad
We all know that it's easy to generate random numbers and strings in C# using the Random class. Or is it, really?

The first time I have ever been tasked to generate random integers was when I was "moving" my head from classic ASP to ASP.NET (in the beginning of 2000, I believe.) Naturally, I used the Random class right in the page's Load handler. It worked fine for a while.

A bit later I needed to generate random strings in another project. Then I realized that Random is not really that "random" if its seed is based on the current time. And it cannot be used in static environments unless you pass it in as a parameter. And it's not really thread safe. So, I've come to realization that randomizing things is not that simple unless you need it for some basic functionality of little importance. Let's discuss the most common ways to generate random stuff.

First off, I'd like to point out that there are two types of randoms: random string and random number. It's important. Secondly, most of the time I use my random strings in a web environment, so I need them to exclude all special chars that might mess up a URL, or some markup, or XML encoding. I also need my randoms to include chars in both cases. And, finally, I need my randoms to exclude certain numbers and chars that may look very similar when displayed in certain fonts. For example, you can hardly tell apart the capital "O" and zero when they are displayed in some sans-serif fonts. The same goes with l, | and 1, "O" and "Q", etc.

Actually, the simplest way to generate random string, I think, is to call Path's GetRandomFileName static method that takes nothing and returns... well... random string:
string random = System.IO.Path.GetRandomFileName().Replace(".", string.Empty);

I have three issues with this method: the strings that it generates are lower case only, you cannot control the length of the resulting string, and it includes all chars and numbers (but not special chars, though). So, if you are fine with those problems then use this method - it cannot get simpler than this.

Another simple way to generate random strings is to use ASP.NET Membership type:
string random = System.Web.Security.Membership.GeneratePassword(10, 0);

Although it's very easy to use, I have a couple of issues with this method as well. First, I must reference the System.Web assembly. This can be annoying if you are building a generic security library, for example. But, of course, it's a small annoyance. The real problem is that the second parameter in the signature of this method is supposed to let you specify how many special chars you want to include in the resulting string. Special chars are actually quite desirable if you need a random string while, for example, resetting user's password ("GeneratePassword", dah) But for some reason the resulting string almost always contains chars like @, %, $, (, [, { and so on if I pass a zero there. It's either I'm completely stupid or the guy who wrote that method had something special for zeros in this method. Something that I managed to miss entirely. I won't even bother reflecting the source to find out why. Zero should mean zero. Period :)

But let's leave strings alone for a while. Any random string generation is based on random number. And the most common way to get random integer in .NET is to use the System.Random class:
System.Random r = new System.Random((int)System.DateTime.Now.Ticks);
int random = r.Next(1, 1000);

This code will produce a random integer from 1 to 1000. The seed in this code is based on the current time (read more about the seed value in class' documentation linked below). Very simple. But let's say you need to generate several random integers in a loop. For example, 100 iterations, each produces an integer from one to ten thousand (we'll store all these random integers in an array):
int[] array = new int[100];
for (int i = 0; i < 100; i++)
{
System.Random r = new System.Random((int)System.DateTime.Now.Ticks);
array[i] = r.Next(1, 10000);
}

If you run this code on your machine and check the content of the array, you'll see that those random integers are not really that random. For example, on my machine (Win 7, i7 CPU, 16 GB RAM) the first 32 members of the array contained the value of 9423 and the last 68 members all contained the value of 7179.

Why? Because I created a new instance of Random type in each iteration, passing the current number of ticks as the seed value. But because each instance uses the same algorithm, the first call to its Next method produces the same value as the instance in the previous iteration did if the seed is the same in both iterations.

Evidently, the seed was the same for the first 32 iterations. Then it changed and continued to be the same for the rest 68 iterations. Why? Well, because my machine runs faster than time :) It managed to run 32 loops in just one tick (one ten-millionth of a second!) And then another 68 loops (twice more) in the next tick. Hense the result.

The common way to avoid this kind of situations is to reuse the same instance of Random type:
int[] array = new int[100];
System.Random r = new System.Random((int)System.DateTime.Now.Ticks);

for (int i = 0; i < 100; i++) array[i] = r.Next(1, 10000);

In this case the seed will be the same for all iterations, not just the first 32. But this time, because we reuse the Random now, each call of its Next method will actually get the "next" value for the same seed.

So, we are done with random integers, right? Not really. The thing is, you will want to reuse this code. Most definitely. And not in just one project - in many projects. Therefore, it makes total sense to declare this code as a public method somewhere in your common framework:
public int GetRandomInteger(int maxValue)
{
return new System.Random((int)System.DateTime.Now.Ticks).Next(1, maxValue);
}

See the issue here? If you call this method several times in a row, each time it will return the same integer for the same reasons we discussed above. To fix that, you could ask for an instance of Random:
public int GetRandomInteger(System.Random random, int maxValue)
{
return random.Next(1, maxValue);
}

This is lame, though. Not only you now have a method that only calls one method of an instance that you just passed as a parameter. You also need to instantiate a Random class each time you need one or more random integers.

What we really need is a reliable way to get a different value of the seed each time we call our method. The only way I know to achieve this is to use the System.Security.Cryptography namespace. For example, take a look at the RNGCryptoServiceProvider class. This class has instance method GetBytes that gets an array of bytes and fills it with cryptographically strong sequence of random values. Precisely what we need!
public int GetRandomNumber(int maxNumber)
{
if(maxNumber < 1)
throw new System.Exception("The maxNumber value should be greater than 1");
byte[] b = new byte[4];
new System.Security.Cryptography.RNGCryptoServiceProvider().GetBytes(b);
int seed = (b[0] & 0x7f) << 24 | b[1] << 16 | b[2] << 8 | b[3];
System.Random r = new System.Random(seed);
return r.Next(1, maxNumber);
}

I declared an array of bytes, filled it with random values using an instance of RNGCryptoServiceProvider and created a seed by converting the lower 31 bits of the array into an integer using big-endian conversion (see the link to Endianness in the References section below). All this guarantees that our seed will be different on each call.

So, we have a method that reliably generates random integers. Consequently, it also solves a huge thread safety problem. If you read community content at the bottom of Random class documentation page you'll find interesting comments and lengthy code samples, all related to thread safety of the Random class when you reuse the same instance in multi-threaded environments. The only way to solve that issue is to "lock" some global instance of some object (commonly called a "semaphore") and call Random's Next() method from inside of that lock. But our method has no such problems, though, because we managed to create a new instance of Random type on each call which is local to the method. Sweet!

Now that we have a way to generate random numbers, let's get back to strings. I'm going to post the entire method here and then explain it in details:
public string GetRandomString(int length)
{
string[] array = new string[54]
{
"0","2","3","4","5","6","8","9",
"a","b","c","d","e","f","g","h","j","k","m","n","p","q","r","s","t","u","v","w","x","y","z",
"A","B","C","D","E","F","G","H","J","K","L","M","N","P","R","S","T","U","V","W","X","Y","Z"
};
System.Text.StringBuilder sb = new System.Text.StringBuilder();
for (int i = 0; i < length; i++) sb.Append(array[GetRandomNumber(53)]);
return sb.ToString();
}

First, I created an array of numbers and letters that I want this method to include in my random strings. This is the beauty of it: you can declare one or more arrays, each for a particular situation, and tell the method to use the one that you currently need by passing it some custom enum value such as RandomType.StrongPassword or RandomType.RandomFilename as a parameter. Or something like that. In this example, though, I'm fine with just one array.

Secondly, I created an instance of string builder and begun appending values to it by getting random integers with my GetRandomNumber method as indexes of the array. That's it!

UPDATE: as Gareth kindly noted in his comment, generating random seed using the RNGCryptoServiceProvider class would be slower than doing the same with the Random class. However, I think that in relative numbers the difference is relatively small and could be ignored by most implementations. Of course, you should test both approaches if speed is your major concern.

Happy programming :)
C# Next »
Comments:
Name (optional):
Comment (URLs are allowed and must start with http:// or https://; all tags will be encoded):
Remaining character count:
SPAMMER? Comments on this site are not included in page source. The stuff that you might post here WILL NOT be indexed by search engines.