A generic interface is an interface parameterized by one or more type parameters. It lets one abstraction describe operations over many possible types. IEnumerable<T> applies this idea to sequences; a distribution can use the same shape to produce values of type T. section 12.3
The smallest distribution interface exposes Sample(), returning a randomly generated T. A discrete extension adds Support() and Weight(T), separating the values that can occur from their relative probabilities. section 12.4 section 12.4
Generic interfaces become useful when the varying type is part of the problem’s structure:
- Model probability distributions independently of whether they produce integers, cards, songs, or other values. section 12.3
- Extend a base contract, as IDiscreteDistribution<T> extends IDistribution<T> with discrete-distribution operations. section 12.4
- Represent operations over a family of expression-node types, as IExprVisitor<T> does for visitor results. section 9.4.1
A typed distribution contract
IDistribution<T> requires only T Sample(). IDiscreteDistribution<T> adds IEnumerable<T> Support() and int Weight(T). DiscreteUniform implements IDiscreteDistribution<int>, wrapping Random while exposing a reusable distribution abstraction. section 12.4
A generic visitor result
IExprVisitor<T> declares Visit methods for each expression subtype. Expr.Accept<T>(IExprVisitor<T>) supplies the missing type information so compiler overload resolution and runtime dispatch select the correct Visit method. section 9.4.1
Listing 12.1 An interface and the discrete uniform distribution
public interface IDistribution<T>
{
T Sample();
}
public interface IDiscreteDistribution<T> : IDistribution<T>
{
IEnumerable<T> Support();
int Weight(T t);
}
public sealed class DiscreteUniform : IDiscreteDistribution<int>
{
private readonly Random rnd = new();
private readonly int max;
public static DiscreteUniform Distribution(int max)
{
if (max <= 0)
throw new InvalidOperationException();
return new(max);
}
private DiscreteUniform(int max) => this.max = max;
public int Sample() => rnd.Next(max);
public IEnumerable<int> Support() => Enumerable.Range(0, max);
public int Weight(int t) => 0 <= t && t < max ? 1 : 0;
}
Inspect the complete C# interface declarations and their discrete uniform implementation. section 12.4

