Thursday, August 18, 2011

Covariance and Contravariance in C# 4.0


What is covariance and contravariance?

Covariance and contravariance allows implicit reference conversion for array, delegate and generic type argument. For e.g. type A is base class and type B is subclass of type A so a = b called as covariance and b=a called as contravariance. Let’s understand by simple example.

Simple covariance example

public class A { }
public class B : A{ }

A a = new A();
B b = new B();

a = b; //covariance
b = a; //contravariance (gives Compile time error)

In above example a=b is called as covariance while b = a called as contravariance. The last line gives compile time error because type ‘a’ can’t convert to type ‘b’.

Array covariance

Arrays supports covariance let’s have a look on below code.

string[] str = new string[10];
object[] obj = str;   //covariance

In above code string array is assigned to object array. Which works fine because string array can implicitly converted to object array.

obj[1] = new object(); //throw runtime error
But when new object assigned to obj then it will throw a run time error.

Generic Interface – Covariance and Contravariance (C# 4.0)

C# 4.0 supports covariance and contravariance for generic interfaces.

Covariance

To support covariance for generic interface need to add ‘out’ modifier as argument with T in interface. The out modifier allows interface as covariance. 

Let’s have a look on below code.

public class A { }
public class B : A{ }
public class D<T> : IMyInterface<T>
{
}

interface IMyInterface<out T> { }

D<B> db = new D<B>();
IMyInterface<A> ia = db; //Covariance (only compiles in C# 4.0)

In above code snippet, Interface of type a allows to reference type b which is possible by adding out modifier to interface.

Contravariance

Type is contravariance when you convert in the reverse direction like type b = a. The ‘in’ modifier should be passed as parameter with interface and it allows interface as contravariant. Let’s have a look on below code.

public class A { }
public class B : A{ }
public class D<T> : IMyContraInterface<T>
{
}
interface IMyContraInterface<in T> { }

D<A> da = new D<A>();
IMyContraInterface<B> ib = da; //Contravariance (only compiles in C# 4.0)

In above example type a is assigned to type b which is possible because ‘in’ modifier passed to interface. The out keyword marks a type parameter as covariant and in keyword marks a type parameter as contravariant.

Generic delegate – Covariance and Contravariance (C# 4.0)

C# supports covariance and contravariance for C# generic delegates also similar to generic interface. To support covariance for generic delegate need to add out modifier with T in interface. The out modifier allows delegate as covariance and the in modifier allows delegate as contravariance. Let’s have a look on below code.

public class A { }
public class B : A{ }

delegate T MyCoVariantDelegate<out T>();
delegate void MyContraVariantDelegate<in T>(T arg);

MyCoVariantDelegate<B> b = () => new B();
MyCoVariantDelegate<A> a = b; //Covariance (only compiles in C# 4.0)

MyContraVariantDelegate<A> a = (a2) => Console.WriteLine("hello");
MyContraVariantDelegate<B> b = a; //Contravariance (only compiles 
                                    in C# 4.0)


In above code snippet, delegate type a allows to reference type b and similar delegate type b allows to reference type a which is possible by adding out and in modifier to delegate respectively.

Covariance and contravariance limitations

1. Covariance and contravariance only supported for generic Interface and generic delegate types. Generic class doesn’t support covariance. Let’s have a look on below code.

public class A { }
public class B : A{ }
public class D<T>
{
}
D<B> db = new D<B>();
D<A> da = db; //compile time error

As per above code, when D<B> type assigned to D<A> it will throw compile time error (Can’t convert type B to type A).

2. Covariance and contravariance only supports if type is reference type. Value types are not supported by covariance let’s have a look on below code snippet.

int i = 10;
double x = i; //implicitly converts int to double

IEnumerable<double> realnumbers = new List<int>(); // compile time error

As per above code, integer can implicitly convert to double but last line will throws compile time error because covariance doesn’t support value types.




See also - 



1 comment:

  1. Covariance basically means that the return value of a method that is referenced by your delegate can have a different return type than that specified by the delegate itself, as long as the return type of the method is a subclass of the return type of the delegate.
    Contravariance deals with the parameters data types rather than return data types.


    .net training

    ReplyDelete