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.
Posted on April 30th, 2008 by Andrew
Under C# / Common Language Infrastructure, Programming