Daily Archives: January 3, 2014

Generic types (part 2, basics, advanced)

We have looked into generic classes in part 1 https://csharphardcoreprogramming.wordpress.com/2014/01/01/generic-types-part-1-basics-advanced/.
On the following day I used a generic method when explaining reflection https://csharphardcoreprogramming.wordpress.com/2014/01/02/reflection-basics-advanced/.

Let’s look at generic delegates more closely. They look neat and make code, as the name says, more generic.

delegate T Calc<T>(T a, T b);
private static int Add(int a, int b) { return a + b; }
private static double Multiply(double a, double b) { return a * b; }

public static void Generics8() {
    Calc<double> lProduct = Multiply;
    Calc<int> lSum = Add;

    Console.WriteLine("Product:" + lProduct(5.0, 2.0));
    Console.WriteLine("Sum:" + lSum(5, 2));
} //

We can also use constraints on delegates:

delegate T Calc<T>(T a, T b) where T : struct;

Generally we know the pattern now. And without much explanation a quick summary should cover all generic types:

// generic delegate
delegate T myDelegate<T>(T t) where T : struct;

// generic method
void myMethod<T, U>(T a, T b, U c) {}

// generic class
class myClass<T> where T : System.IComparable<T>, IEnumerable<T> {}

// generic interface
interface IMyInterFace<out TResult, in TInput> {
    TResult DoSometing(TInput Args);

Interfaces can make use of the in and out keywords.
The out keyword declares a generic type parameter covariant. The in keyword makes it contravariant.
Covariance and contravariance were introduced when I described delegates https://csharphardcoreprogramming.wordpress.com/2013/12/16/delegates-basics/ .
The out keyword can only be used as an input parameter if it is a contravariant generic delegate.

interface ICovariant<out T> {
    void DoSomething(Action<T> xCallback);

The contravariant type (in) can only be used in method arguments and not as return types, it can also be used for generic constraints.

interface IContravariant<in T> {
    void DoSomething<U>() where U : T;

There are certain rules about inheritance.
A covariant class cannot inherit from a contravariant class.
A contravariant class cannot inherit from a covariant class.

interface ICovariant<out T> { }
interface IContravariant<in T> { }

// all ok
interface IInvariant1<T> : ICovariant<T> { }
interface IInvariant2<T> : IContravariant<T> { }
interface IInvariant3<T> : ICovariant<T>, IContravariant<T> { }
interface ICovariant1<out T> : ICovariant<T> { }
interface IContravariant1<in T> : IContravariant<T> { }

// compiler error. 
//interface ICovariant2<out T> : IContravariant<T> { }
//interface ICovariant3<out T> : ICovariant<T>, IContravariant<T> { }
//interface IContravariant2<in T> : ICovariant<T> { }
//interface IContravariant3<in T> : ICovariant<T>, IContravariant<T> { }

There is no generic type for Arrays. Backward compatibility did not allow an evolution here. Arrays are very basic, they can be used for high-performing applications. If Microsoft would make them more complex, we would somehow lose that benefit. My advice is to use generic collections/Lists instead. You can use T[], but this is rather a strongly typed array than a generic array.

public static void Generics9<T>() {
    T[] lArray = new T[10];
    foreach (T lElement in lArray) {
} //