The System.Random Class Should Have an Abstract Base Class or Interface

The base class library has a significant flaw with respect to System.Random: there is no abstract base class or interface for random number generators with the same interface as System.Random. (The System.Security.Cryptography namespace does have the RandomNumberGenerator base class, but that type’s interface differs from System.Random‘s.)

The situation is as though someone designed a type hierarchy for representing dogs as follows: instead of creating an abstract base class called Dog, they made an instantiable type called OrangeDog and forced everyone to derive from that. This hierarchy is problematic: it corresponds to the statement that all dogs are orange dogs, and it requires that all Dog instances unnecessarily store and possibly initialize data about OrangeDog instances.

If you want to create a new random number generator type that can be passed to existing methods that accept arguments of type Random, you are forced to derive from Random – the orange dog of the random number generator hierarchy. The new type must carry around data about Random instances, even though this data may have nothing to do with the type being defined and may never be used. Another bad consequence of having no base class for random number generators is that it causes people’s thinking about Random to become muddled. For example, people become tempted to create their own base classes or interfaces. Try a web search for C# RandomBase.

It may not be too late for standards parties such as Microsoft, Mono, and ECMA to add a base class or interface for System.Random, and modify System.Random to derive from it. A new base class could look like the following.

public abstract class RandomBase {
  public abstract int Next();
  public abstract int Next(Int32 maxValue);
  public abstract int Next
    (Int32 minValue, Int32 maxValue);
  public abstract void NextBytes(Byte[] buffer);
  public abstract double NextDouble();

The RandomBase type captures Random‘s public interface (without constructors).

Existing methods that accept a Random instance would continue to work without modification. They would be unable to accept random number generators that derive from RandomBase and not Random. This might be fine, particularly since RandomBase contains all the public Random methods that manipulate constructed Random instances. As long as code is not using reflection, serialization, or something outside the public interface of Random, that code would continue to work when a straight substitution of Random to RandomBase is applied. The vast majority of RandomBase instances would be System.Random instances – most code would not need modification. Code where it is essential to use different generators, as in simulations, libraries, and statistical tests of random number generators, could use RandomBase.

Incidentally, the argument name maxValue in Next(Int32 maxValue) is confusing and technically incorrect or misleading at best, because Next never returns a value of maxValue (unless maxValue is 0). A better name might be upperBound. I have been using the upperBound mnemonic for years to indicate that the upper end of an interval is not inclusive. While upperBound does not indicate whether the range is inclusive or exclusive, it is at least not wrong, and, if used consistently, does clarify semantics. If and when RandomBase is added to the base class library, that might be a good time to improve the maxValue argument’s name.