Friday, June 27, 2008

Covariance and Contravariance in C#2

I was reading Jon Skeet's C# in Depth the other day. Two terms, covariance and contravariance, were discussed in the book. I thought it would be helpful to summarized them here.

So what is covariance? Say, classB is derived from classA. Then, the following statement is valid in C#. The variable, array, can accept a more specific object. That is, arrays of reference-types in C# are convariant.

classA[] array = new classB[0];

However, there is some limit for you to do so in C#2. Convariance in generics is not supported. Generics is invariant and the following statement is not valid in C#2.

List<classA> list = new List<classB>;

Then, what is contravariance? Say, a function, func, is defined in classB as follows.

public classB : classA {

public int func(classB arg) { ... }

}

If contravariance were supported in C#, the following statements should be valid. The function, func, can accept a more general parameter and the parameter of func is said to be contravariant.

classA a = new classA();
classB b = new classB();

b.func(a); //not supported in C#

However, it's not supported this way in C#. C# supports contravariance in delegate and the following code snippet is valid.

void ProcessEvent(object sender, System.EventArgs e) { ... }

this.textBox1.KeyDown += this.ProcessEvent;
//valid in C#2
this.button1.MouseClick += this.ProcessEvent; //valid in C#2
One should note that KeyDown event takes KeyEventArgs which is derived from EventArgs while MouseClick event takes MouseEventArgs which is also derived from EventArgs. So an event in C# can accept a delegate whose signature contains more general parameters.

Further readings:

4 comments:

Jon Skeet said...

How clear is the book on this? It's a tricky topic to talk about - although one point that Eric made when tech reviewing it is that discussions are easier with simple concrete examples like "Banana" and "Fruit".

If you have any suggestions for improvements, I'd be very glad to hear them (and any other comments on the book).

Jon Skeet said...

One extra comment - your example of contravariance doesn't really work because that code *is* valid C#. You really need to introduce delegates to show it properly, and then you can show that covariance/contravariance isn't supported for delegates in C# 1, but *is* supported in C# 2.

For instance:

delegate void ActionOnString(string x)
public void DoSomething(object o) {}

// Invalid in C# 1, valid in C# 2:
ActionOnString action = new ActionOnString(DoSomething);

Ken Wu said...

Jon,

Thanks for the comments. For the books I have read, you are 'the' only one who wrote about the topic on contravariance in C#. Contravariance is so counterintuitive that it really took me a while to understand. Then I know why few people talk about it. It's not easy to explain but you made it. Good job!

mahakk01 said...

This post explains Covariance and Contravariance in C#2. Both terms are very well explained with the help of syntax and example. This is the best way you can explain these terms. I really like your post. This is very well written post.
sap erp download