Generics

Generics overview

Generics enable writing of code abstractions which can be applied to different types at compile time. List, for example, is a basic list abstraction which allows for type-safe storage of values. Using a “List” type reference creates a specialized int32 list type.

Methods can also have generic parameters, allowing for them to be specialized either explicitly or implicitly based on callsite argument types.

public static T GetFirst<T>(List<T> list)
{
    return list[0];
}
...
let intList = new List<int32>();
intList.Add(123);
let firstVal = GetFirst(intList);

Generic constraints can be specified, which describe the ‘shape’ of the type which the generic code is intended to work with.

public static T Abs<T>(T value) where T : IOpComparable, IOpNegatable
{
    if (value < default)
        return -value;
    else
        return value;
}
/* This method can eliminate runtime branching by specializing at compile time by incoming array size */
public static float GetSum<TCount>(float[TCount] vals) where TCount : const int
{
    if (vals.Count == 0)
    {
        return 0;
    }
    else if (vals.Count == 1)
    {
        return vals[0];
    }
    else
    {
        float total = 0;
        for (let val in vals)
            total += val;
        return total;
    }
}
static TTo Convert<TTo, TFrom>(TFrom val) where TTo : operator explicit TFrom
{
    return (TTo)val;
}

/* We use partial explicit generic args to allow inference of 'TFrom' */
var val = Convert<int...>(1.2f);