Covariance, Contravariance and Generics

This post is a result of an expensive lesson I learned. 

The situation:  We use an MVP style architecture, Presentation Abstraction Control, on our application.  As such the controller maintains a reference to the view(presentation) and the model(control) via IView and IModel references.  As such any time we want to invoke a method on the model or the view we have to do something like this:

WireViewEvents((IOrderDetailView)this.View);

I'll draw your attention to the casting that happens.  Since the controller is handling all the interaction between the view and the rest of the application, you can imagine how this type of casting ends up in all of our code.

We first took a stab at the casting 'problem' by implementing a private property on the controller to take care of the casting for us.  Then we could handle the above by doing something like:

WireViewEvents(this.PrivateView);

This works but it had a smell of hoakie-ness to it.  So I thought about trying to come up with a 'Generic' solution.  My idea was brilliant if I say so myself ;).  I decided to change the definition of the controller to be a generic controller that took in the type of view and the type of model on creation.  That way each of the view and model properties would be strongly typed. 

I proceeded to implement this in our code base since it was not easy to just make one change.  Everything compiled but once I was done the application started throwing exceptions.  It seemed that IController<ConcreteModel, ConcreteView> could not be passed to an instance declared as IController<IModel, IView>.  Say what?!?!  At first I didn't get it.  After some research, I found out that Generics do not support covariance and contravariance.  

What is covariance/contravariance?  In a nutshell, they are terms used to describe how types relate to each other.  The names come from the math world and is used to measure the degree to which two variables move up or down together.  The term has been adopted by CS geeks to describe a situation where a derived type is used when a base type was expected (MSDN). 

The core issue has apparently been documented by the community in many details but the short of it is that Generics are not covariant.  For one of the best write ups that I have seen on the subject, Rick Byers wrote an article that covers it.  You can see it here and his follow up here.

 Lesson learned, had I just RTFM this would have not taken me 6 hours to learn!

References:
MSDN - http://msdn.microsoft.com/msdnmag/issues/05/10/NETMatters/

[ Currently Playing : Lonely Avenue - Taj Mahal - Phantom Blues (03:30) ]
Published Monday, July 31, 2006 3:54 PM by dotnetgeek
Filed under: ,

Comments

# re: Covariance, Contravariance and Generics

Monday, July 31, 2006 2:56 PM by Rick Byers
I'm glad you found my post usefull. You didn't describe what you ended up doing. Did you just give up and go back to casting? My gut feeling is that there should be an elegant generic solution for this sort of design problems. As you know, the power of generics is that they let you deal with more precise type information at compile-time. Equivalently, the burden of generics is that they force you to deal with precise type information at compile-time. Dynamic (run-time bound) patterns you're used to finding natural won't work with generics precisely because generics are an attempt to replace some run-time decisions with compile-time decisions (therefore forcing you to be consistent about those decisions in the source-code). Anyway, some things to think about for a solution here: If you really nead variables of type "IController", perhaps IController should inherit from a non-generic interface IController (the same way IEnumerable inherits from IEnumerable). You won't be able to refer to the exact type of model and view in the non-generic interface, but that is precisely the point - you don't know it! If you want to be able to handle cases where you work with a specific, but undetermined, type of model or view, then you may want a generic method (see the example in my follow-up blog post). I hope this helps, Rick

# re: Covariance, Contravariance and Generics

Monday, July 31, 2006 10:29 PM by dotnetgeek

Rick,

Indeed I did go back to casting. Now re-reading your follow-up post (2 more times), I think that the solution that you are proposing may actually work.  Ill have to play around with when I would use the type as a non generic interface vs. a generic interface.

Thanks for the pointer!

Rich

# Covariance, Contravariance and Generics Part Deux

Tuesday, August 08, 2006 9:04 PM by The Original .NET Geek

So I just wanted to follow up on my last post where I described in detail (agonizing) how I thought it

Powered by Community Server (Non-Commercial Edition), by Telligent Systems