Generics Variance: Covariance, Contravariance and Invariance

by Ivan Hamilton 12/6/2007 5:23:00 PM

There's alot of things you don't think about until... you have to think about. I recently came across generics variance in C# (or lack of it). Best explained with a quick example.

Firstly get yourself a simple structure:

    class Animal { }

    class Mammal : Animal { }

    class Dog : Mammal { }

    static void AnimalMethod(Animal animal) { }

    static void AnimalArrayMethod(Animal[] animal) { }

    static void AnimalListMethod(List<Animal> animal) { }

Then do something very simple:

    AnimalMethod(new Dog());

    AnimalArrayMethod(new Dog[0]);

    AnimalListMethod(new List<Dog>());

Just think about that for a moment. Is it safe to pass a reference to a Dog for an Animal parameter? Can a Dog[] be treated as an Animal[]? Does it make sense to pass a List<Dog> for a List<Animal>?

Yes, no & maybe. (Compiler: AnimalMethod & AnimalArrayMethod calls are valid and AnimalListMethod call is invalid)

Let's look at the above examples:

  • AnimalMethod - Simple reference conversion: Nothing new here.
  • AnimalArrayMethod - Array Covariance: A controversial C# feature. If I pass a Dog[] thru an Animal[] parameter, it's valid at compile time for that method to assign a Cat into the array. Because of array covariance, assignments to elements of reference type arrays include a run-time check that ensures that the value being assigned to the array element is actually of a permitted type.
  • AnimalListMethod - Generic Covariance: A non-existant feature... but does it make sense? Well... as much sense as array covariance.

The compile time validity of array & generic variance would depend upon the action being performed. If we were to only read an element, covariance is safe (each Cat can be treated as an Animal). If we only write, contravariance is safe (into Animals we can place a Cat). If we read and write, then invariance is safe (only Cat into Cats and Cat from Cats).

C# array variance is broken. Why? Because it's unbalanced: covariance is supported while contravariance is not. For every instance you may want covariance to support passing in subtypes, there's a case for contravariance to pass out to supertype referenece.

C# generic constraints are broken. Why? Because it's unbalanced: subtypes are supported while supertypes are not. For every instance you may want subtypes to support passing in subtypes, there's a case for supertypes to pass out to a supertype referenece.

I don't know if variance be can "fixed". Any changes to existing array covariance, would probably make far too many people have to think about their code. Compiler designers hate breaking changes (new compiler won't compile old code). You'd also need language extensions to allow you to declare whether you permit covariance/contravariance at certain points.

I doubt it will get addressed... we must learn to live with what our compiler allows. Human sacrifice, dogs and cats living together - mass hysteria.

P.S. Eric Lippert has a brilliant set of posts on Covariance and Contravariance.

Currently rated 3.0 by 5 people

  • Currently 3/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5



Related posts


Add comment



Powered by BlogEngine.NET
Original theme by Mads Kristensen

About the author

Name of author Ivan Hamilton
"My inner nerd can beat up your inner nerd."

E-mail me Send mail



<<  April 2018  >>

View posts in large calendar

Recent comments





    The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

    © Copyright 2018

    Sign in