Monday, April 13, 2009 12:41 AM bart

C# 4.0 Feature Focus – Part 4 – Co- and Contra-Variance for Generic Delegate and Interface Types

Last time around in this series, I promised to talk about generic co- and contra-variance. So that’s why we’re here today. In this post I’ll explain the theoretical concepts behind these fancy-sounding terms, look at the runtime’s take on it, show how to use them in C# 4.0 and most important of all: tell you why you don’t have to worry about all of this :-).

What’s variance?

Language features with names like variance ought to come from a theoretical background, don’t you think so? Absolutely right, and that’s no different for variance with category theory being its source of inspiration. Although we often speak about co- and contra-variance (turns out C# – amongst lots of other languages – already use those concepts , as we’ll see later) there are three sorts of variance to be explained before moving on.

Type ordering

First we need to establish the notion of type ordering. All of you know about object-oriented programming where types are used to indicate the “kind” of data one is dealing with. On top of this, subtyping is used to specialize types. Or, the other way around, operations supported on a supertype can also be applied to its subtypes (possibly with a specialized implementation through overriding and virtual method dispatch).

For example, System.Object has a ToString method. System.DateTime is a subtype of System.Object and therefore also has a ToString method on it (either derived from the base type as-is or overridden, but that doesn’t matter for now).

Let’s use more concise notation to write down such relationships. If D(erived) is a subclass of B(ase), we’ll write D <: B. To say d is an instance of D, we’ll write d : D.

Based on this we can start formulating properties, such as subsumption, the formal notion of subtype-relationships:

If d : D and D <: B, then d : B

For instance, if we substitute d for name, D for System.String and B for System.Object, we end up with:

If name : System.String and System.String <: System.Object, then name : System.Object

We all know this: because name is declared to be a string and the string type is a subtype of object, we can say name ought to be an object. The notation <: is what we call type ordering. I’ll leave it to the math-savvy reader to map those concepts back to the properties of binary relations, such as reflexivity, (anti)-symmetry and transitivity.


What’s up with variance? In short, variance is a property of operators that act on types. This is very abstract, but we’ll make it very concrete in just a second. For now, think of it very generally: assume op(T1, T2) is an operator “that does something with/based on” T1 and T2 which are both types. For mathematicians (and programmers as it turns out) it makes perfect sense to attribute properties to that operator, capturing its behavior with respect to the types it acts on.

To make things a bit more concrete, let’s map the concept of operators onto generics. (Note for geeks only. Generics are a form of “parametric polymorphism”, which extends the lambda calculus to something known as System F.) As we know, generics allow for type parameterization. Below is a simple example introducing a Pair type:

class Pair<TFirst, TSecond>
    public Pair(TFirst first, TSecond second)
        First = first;
        Second = second;

    public TFirst First { get; private set; }
    public TSecond Second { get; private set; }

You can think of this type declaration as an operator that, given type values for TFirst and TSecond, can give us a constructed type back (in other words, it’s a type constructor, in the Haskell sense, not the CLR sense). Now we want to be able to express type ordering relationships over such constructed generic types. For example, how does Pair<string, int> relate to Pair<string, object> or to Pair<DateTime, int> or to … whatever. More specifically, we want to be able to infer the relationship between any Pair<T1, T2> and Pair<T3, T4> from relationships between the parameters.

For instance, can we say (this is a question, not an answer just yet…) that:

If T1 <: T3 and T2 <: T4, then Pair<T1,T2> <: Pair<T3,T4>

These are the kind of questions asked by theorists, but obviously by developers today as well.


Time to dive into the first kind of variance: covariance. Co as a prefix means “together with”. It means an operator preserves the ordering of types, when compared to its operands. Let me give a concrete sample, based on our Pair class above.

If you know that Apple is a subtype of Fruit (Apple <: Fruit) and Tomato is a subtype of Vegetable (Tomato <: Vegetable), can you also say that Pair<Apple, Tomato> is a subtype of Pair<Fruit, Vegetable>? Looks like that ought to be valid on first sight, no? But there might be some hidden caveats… We’ll get to that soon.

The formalized notion of covariance can be stated as follows:

Operator op(T1, …, Tn) is covariant in Ti (1 <= i <= n) if Si <: Ti implies op(T1, …, Ti-1, Si, Ti+1, …, Tn) <: op(T1, …, Tn)

A very practical case where this comes up is with sequences. For instance, given a LINQ query that returns a sequence of Student objects, can we treat that sequence as one of Person objects, i.e.:

IEnumerable<Person> people = from Student s in db.Students
                             where s.Age <= 25
                             select s;

Today, in C# 3.0, you cannot do this because IEnumerable<T> is not covariant in T.


The opposite of “co” is “contra” which means “against”. In the context of variance it means an operator reverses the ordering of types, when compared to its operands. Again a concrete sample is more than welcome I guess.

Let’s stick with fruits. We known – just repeating this obvious fact – that Apple is a subtype of Fruit (Apple <: Fruit). Now say if we want to carry out comparison between apples (IComparer<Apple>), is it possible then to use a comparison between fruits instead (IComparer<Fruit>)? Looks like that should be possible, right? Everything that can handle two pieces of fruit for comparison ought to be able to handle two apples as each apple is a piece of fruit.

The formalized notion of contravariance can be stated as follows:

Operator op(T1, …, Tn) is contravariance in Ti (1 <= i <= n) if Si <: Ti implies op(T1, …, Ti-1, Si, Ti+1, …, Tn) :> op(T1, …, Tn)

A very practical case where this comes up is with action delegates. For instance, given an action that takes in a Person, can we treat that action as one that deals with a Student object instead, i.e.:

Action<Student> submitLetter = (Person p) => { 

Update: I made a mistake in my original post, where I swapped Person and Student in the sample above. Guess it was getting late and despite double-checking all uses of the terminology, it slipped through. Thanks to Chris for pointing this out!

Today, in C# 3.0, you cannot do this because Action<T> is not contravariant in T.

Broken array variance

First a shock to the reader: arrays in the CLR are covariant. “So what?”, you might wonder. Well, and here comes the shock, it turns out covariant use of arrays is not type-safe. (Let you heartbeat go down before reading on.) We’ll get to why covariant treatment of arrays is broken, but first some history. You might assume this “broken array variance” was put in the CLI (Common Language Infrastructure, the standardized specification of the CLR, ECMA-335) intentionally. The mission of the CLI was – and still is, bigger than ever with the whole DLR on top of it – to accommodate executing different languages on the same unified runtime with a unified type system, instruction set, runtime services, etc. One such language was Java, which has broken array covariance, and being able to accommodate that language won over fixing this behavior.

But what makes arrays unsafe for covariance? Another sample with fruits… The story of the fruit farmer.

A fruit farmer produces apples and has a contract with a local grocery store to sell the apples. To do so, the farmer hands over an array of apples to the store. The contract states the store can return the apples that haven’t been sold in the next week, and only the apples that were sold (indicated by empty spots in the array from which the pieces of fruit have been taken) will be billed. The remainder apples – possible rotten by now – are sent to a juice factory nearby that creates sweet apple juice. This scheme is illustrated below:


But because arrays are covariant, the grocery story (on the right) can treat an array of Apples (Apple[]) as if it were an array of Fruits (Fruit[]). Since the cells of arrays are writeable, this opens up a potential hole in the system. What if the grocery store wants to cheat and put some rotten tomatoes in the tray (let’s assume tomatoes are fruits for sake of this discussion; if you don’t agree with this, substitute “rotten tomato” for “rotten lemon” but that doesn’t sound as bad IMO, hence the use of tomatoes). The contract with the farmer stated that only the number of empty places in the tray will be considered in billing the grocery story; so fill a few empty places with unsellable rotten tomatoes and the price gets reduced. This might go unnoticed if the farmer doesn’t enforce runtime fruit/vegetable type safety.

If the contract with the juice factory states that only Apple-arrays can be passed in, but this didn’t get checked by the farmer at runtime after return from the grocery store, their juice will now contain tomato juice as well (a strange combination I guess). Or worse, the juice factory will blow up because the apple peeling machine expects a certain toughness from the apple being peeled and tomatoes are much softer (note: I don’t know anything at all about the factory process involved in creating juice, so I’m just fantasizing about possible horror stories).

This mishap is illustrated below.


But who to blame? The grocery story followed the array-based contract exactly; arrays do not prevent writing operations, so putting tomatoes in is not a violation. Clearly the farmer needs a tighter contract that ensures – statically – the grocery store cannot put other fruits or vegetables in. What the farmer should use is an “pull-based enumerating device for apples” (an IEnumerator<Apple> that is) as shown below:


The spring depicts the enumerating behavior – you can only get things out but can’t push things in (ignore forceful mechanisms and ignoring the fact the spring might crush the apples :-)). Such an IEnumerable<T> is safely covariant because you can’t push anything in, so even if the farmer treats the IEnumerable<Apple> as an IEnumerable<Fruit> all he can do is get pieces of fruit (which always will happen to be apples) out.

This illustrates why arrays T[] are not safely covariant and why IEnumerable<T> is. Or in code:

namespace Rural.Fields
    using Market;
    using Industry;

    internal sealed class Farm
        private GroceryStore _store = …;
        private JuiceFactory _juice = …;
        private const decimal APPLE_PRICE = 0.5;

        private Apple[] PluckAppleTrees() { … }

        public void Work()
            Apple[] apples = PluckAppleTrees();
// here the array is treated covariantly!
            int sold = apples.Where(apple => apple != null).Count();
            _store.SendInvoice(sold * APPLE_PRICE);
// here the array is treated invariantly
            _juice.SendInvoice((apples.Length – sold) * APPLE_PRICE * 0.8 /* price of rotten apple */);

namespace Market
    public sealed class GroceryStore
        public void SellFruits(Fruit[] fruits) { … }
        public void SendInvoice(decimal amount) { … }

namespace Industry
    public sealed class JuiceFactory
        public void ProduceAppleJuice(Apple[] apples) { … }
        public void SendInvoice(decimal amount) { … }

To ensure type safety for covariant arrays, the CLR injects runtime type checks for every attempt to store an element in an array. If an object with a type incompatible with the array in memory is attempted to be written to the array, an ArrayTypeMismatchException occurs. In our case above, if the SellFruits method of GroceryStory would try to write a Tomato object to the array that’s passed in (as Fruit[] but that was created as an Apple array, in the PluckAppleTrees method of the Farm) the CLR would detect this and throw the exception.

A more isolated sample:

string[] names = new string[] { “Bart”, “John” };
object[] things = names;
things[0] = 123;
// ArrayTypeMismatchException – 123 is not a string
Console.WriteLine(names[0].ToUpper() /* if the above would work, we’d call the non-existing ToUpper method on System.Int32 */);

Today’s co- and contra-variance support

C# actually has places where co- and contra-variance principles are being used, more specifically in delegate types. A typical sample where this comes in useful is when dealing with events.

delegate void CancelEvent(object sender, CancelEventArgs e);
delegate void ProgressEvent(object sender, ProgressEventArgs e);

sealed class Engine
    public event CancelEvent Cancelled;
    public event ProgressEvent ProgressChanged;

    public void Run() { … }
    public void Cancel() { … }

static class Program
    static void Main()
        var engine = new Engine();
        engine.Cancelled += Logger;
        engine.ProgressChanged += Logger;
// run engine, etc

    void Logger(object sender, EventArgs e) { … }

Here we’re attaching an event handler to two events which have different signatures. However, the signature of Logger uses a common supertype of both events’ event arguments type, so the compiler allows this. The reason it does is because input parameters can safely be treated contravariantly. In other words, we’re getting in a more derived type for the event arguments but we treat it as less derived. As the argument appears in an input position, this is safe to do.

Here’s a little exercise for the reader. The following fragment compiles fine because of contravariant treatment for delegate parameters:

class Contra
    static void Main()
        var b = new Bar();
        b.Event += Handler;

    static void Handler(object o /* contravariant treatment when used with delegate type D */)

delegate void D(string s);

class Bar
    public event D Event;

However, if we change the parameter on D by adding a ref modifier, it doesn’t work anymore.

static void Handler(ref object o /* ??? */)

delegate void
D(ref string s);

Why? Illustrate with a sample showing why contravariant treatment would be malicious if it were allowed here…

Covariant and contravariant generic parameters in the CLI

With the second release of the CLR, as part of .NET Framework 2.0, generics were introduced based on the work done by Don Syme and Andrew Kennedy in the Gyro project. Right from the start, generic parameters have supported the declaration of desired variance behavior. Section 8 in Partition I of the ECMA-335 spec for the CLI states:

In addition, CLI supports covariant and contravariant generic parameters, with the following characteristics:

  • It is type-safe (based on purely static checking)
  • Simplicity: in particular, variance is only permitted on generic interfaces and generic delegates (not classes or value-types)
  • Languages not wishing to support variance can ignore the feature, and treat all generic types as non-variant.
  • Enable implementation of more complex covariance scheme as used in some languages, e.g. Eiffel.

but so far, C# and VB have been following the third bullet, ignoring the feature. Before we go there, we should have a look at how generics variance is surfaced through IL, proving its support in the CLR today. More information on this can be found in paragraph 9.5 of Partition II:

The CLI supports covariance and contravariance of generic parameters, but only in the signatures of interfaces and delegate classes. 
The symbol “+” is used in the syntax of §10.1.7 to denote a covariant generic parameter, while “-” is used to denote a contravariant generic parameter.

Our two main samples have been IEnumerable<T> and IComparer<T>. Let’s define our own interfaces for both (in C#) and see how it looks like at the level of IL:

interface IEnumerable<T>
    IEnumerator<T> GetEnumerator();

interface IEnumerator<T>
    bool MoveNext();
    T Current { get; }

interface IComparer<T>
    int Compare(T arg1, T arg2);

By default generic interface and delegate types are invariant:


However, we can roundtrip through IL to make those types covariant (for IEnumer*<T>) or contravariant (for IComparer<T>)in their generic parameter T. The difference is subtle: adding a + (covariant) or a - (contravariant) to the generic parameter T.

.class interface private abstract auto ansi IEnumerable`1<+T>
  .method public hidebysig newslot abstract virtual
          instance class IEnumerator`1<!T>
          GetEnumerator() cil managed
  } // end of method IEnumerable`1::GetEnumerator

} // end of class IEnumerable`1

.class interface private abstract auto ansi IEnumerator`1<+T>
  .method public hidebysig newslot abstract virtual
          instance bool  MoveNext() cil managed
  } // end of method IEnumerator`1::MoveNext

  .method public hidebysig newslot specialname abstract virtual
          instance !T  get_Current() cil managed
  } // end of method IEnumerator`1::get_Current

  .property instance !T Current()
    .get instance !T IEnumerator`1::get_Current()
  } // end of property IEnumerator`1::Current
} // end of class IEnumerator`1

.class interface private abstract auto ansi IComparer`1<-T>
  .method public hidebysig newslot abstract virtual
          instance int32  Compare(!T arg1,
                                  !T arg2) cil managed
  } // end of method IComparer`1::Compare

} // end of class IComparer`1


Notice the ilasm tool doesn’t statically verify the correct use of variance annotations. It’s possible to mark a generic parameter as covariant while it’s used in input positions. It’s the responsibility of language compilers, e.g. for C#, to enforce such rules:

  • Covariant parameters should only be used in output positions: method return values, get-only properties or indexers.
  • Contravariant parameters should only occur in input positions: method parameters, set-only properties or indexers.

C# 4.0 support

Starting with C# 4.0, the language does support co- and contra-variance on generic delegate and interface type parameters. This feature has two sides to it:

  • As a consumer of generic interface or delegate types that behave either co- or contra-variantly, you can now do what you couldn’t do before

IEnumerable<Person> people = from Student s in db.Students
                             where s.Age <= 25
                             select s;

IComparer<string> comp = new MyObjectComparer(); // implements IComparer<object>

  • As a provider of generic interface or delegate types you can now specify the intended behavior. This is done using the out and in keywords, which respectively stand for covariant (output positions only) and contravariant (input positions only). The compiler enforces that parameters that behave in a non-invariant way are used appropriately:

interface IEnumerable<out T>
    IEnumerator<T> GetEnumerator();

interface IEnumerator<out T>
    bool MoveNext();
    T Current { get; }

interface IComparer<in T>
    int Compare(T arg1, T arg2);

You might wonder why the compiler doesn’t infer the applicable variance annotation on behalf of the user. This could easily lead to “accidental” incorrect treatment as things evolve. Although it’s not a good idea to start changing interfaces, during development your interface types might be in flux and it would be disruptive if consumer code starts breaking all of a sudden because a generic parameter’s variance treatment has changed. It was felt it’d be better to have developers be explicit about variance.

Obviously a bunch of interface and delegate types in the BCL have been modified with variance annotations. IComparer<T>, IEnumerable<T> and IEnumerator<T> are just a few samples. Others include the Func and Action delegates:

delegate R Func<in T1, in T2, …, in Tn, out R>(T1 arg1, T2 arg2, …, Tn argn);
delegate void Action<in T1, in T2, …, in Tn>(T1 arg1, T2 arg2, …, Tn argn);

That’s it. I told’ya it was simple, no? The good thing is, you shouldn’t really know much about all of this: It Just Works (now also available for C# – inside joke). | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Filed under:


# re: C# 4.0 Feature Focus – Part 4 – Co- and Contra-Variance for Generic Delegate and Interface Types

Monday, April 13, 2009 3:16 AM by Olmo

I think the contravariance example is just the opposite. Isn't it?

Action<Person> submitLetter = (Student s) => {

   if (s.InDorm)

       SendMailTo(s.University.Address, s.StudentId);




# re: C# 4.0 Feature Focus – Part 4 – Co- and Contra-Variance for Generic Delegate and Interface Types

Monday, April 13, 2009 3:38 AM by Haytham

useful, to know more about other C Sharp 4 features. go there:

# C# 4.0 Feature Focus – Part 4 – Co- and Contra-Variance for Generic Delegate and Interface Types - B# .NET Blog

Monday, April 13, 2009 4:20 AM by DotNetShoutout

Thank you for submitting this cool story - Trackback from DotNetShoutout

# re: C# 4.0 Feature Focus – Part 4 – Co- and Contra-Variance for Generic Delegate and Interface Types

Monday, April 13, 2009 6:46 AM by Vadim

Thanks for a wonderful post! At last I got this stuff.

Anyway, how can a string comparer compare arbitrary objects?

or how can we treat a generic person if we know how to deal with a student?

IMO these samples violate the definition of contra-variance

String <: Object, so IComparer<Object> <: IComparer<String>, not the other way round!

Don't they?

# C# 4.0 – часть 4 – Co- и Contra-Variance для Generic Delegate и Interface-типов

Monday, April 13, 2009 8:13 AM by

Thank you for submitting this cool story - Trackback from

# re: C# 4.0 Feature Focus – Part 4 – Co- and Contra-Variance for Generic Delegate and Interface Types

Monday, April 13, 2009 8:21 AM by Pedantic biologist

Tomatoes are both fruit & vegetable.

# re: C# 4.0 Feature Focus – Part 4 – Co- and Contra-Variance for Generic Delegate and Interface Types

Monday, April 13, 2009 5:08 PM by bart

Hi Olmo, Vadim,

There was a mistake in my original write-up indeed; it's so easy to get those things wrong. Chris was the first to correct me per mail, but thanks to both of you for spotting this as well (great to see some people actually read my epistles). Anyway, it has been corrected earlier today and here's the right explanation:

Given a comparer for objects, can I use it where a comparer of strings is expected? Sure, since every string is an object (notice the opposite order compared to the first sentence). Why this is safe to do is because we're only feeding things in to the interface.



# Summaries 11.04.2009 &ndash; 13.04.2009 &laquo; Bogdan Brinzarea&#8217;s blog

Pingback from  Summaries 11.04.2009 &ndash; 13.04.2009 &laquo; Bogdan Brinzarea&#8217;s blog

# re: C# 4.0 Feature Focus – Part 4 – Co- and Contra-Variance for Generic Delegate and Interface Types

Wednesday, April 15, 2009 4:33 PM by Eber Irigoyen

so shouldn't the IComparer example be bacwards?

IComparer<object> comp = new MyObjectComparer(); // implements IComparer<string>

instead of

IComparer<string> comp = new MyObjectComparer(); // implements IComparer<object>

# re: C# 4.0 Feature Focus – Part 4 – Co- and Contra-Variance for Generic Delegate and Interface Types

Wednesday, April 15, 2009 9:28 PM by bart

Hi Eber,

Contravariance can be a good brainteaser and easy to get wrong (as happened to me <g>). However, the IComparer sample is okay as it is today.

Say you have something that can compare two arbitrary objects (whether or not there is a meaningful comparer that does that is another thing, just assume it can compare for "equality"). Now some API wants something that can compare two strings. Sure enough I could pass in my object-comparer: every string is an object, so anything that can compare objects can also compare strings.

Of course a more specialized comparer might be useful in this scenario, e.g. some that compares strings - the most specific type we have at hand - in some way, but that's irrelevant for this discussion.

My sample is as minimalistic as it possibly can be; look at it in a broader context as follows:

void IWantToCompareStrings(IComparer<string> comp) { ... }

You want to call this guy. All _it_ can do by itself is feed *in* two string objects to the supplied comparer object. In order for _me_ to call _it_ I need to supply a way to compare two strings. But I could say, let's be more generic: I get strings in but nothing forbids me from treating those as a less-derived type, e.g. System.Object in this case. So why shouldn't I be able to supply an IComparer<object> instead?

IComparer<object> objectComparer = new /* hypothetical syntax */ IComparer<object> {

   int CompareTo(object a, object b) { ... }



Does this help?


# re: C# 4.0 Feature Focus – Part 4 – Co- and Contra-Variance for Generic Delegate and Interface Types

Wednesday, April 15, 2009 11:40 PM by Jarrett

The out and in keywords are not new obviously, "in" is in foreach(x in a) and "out" is used to pass a value out from a method, but I was wondering if these words are superficially the same or if they are the same underlying concept?

# re: C# 4.0 Feature Focus – Part 4 – Co- and Contra-Variance for Generic Delegate and Interface Types

Thursday, April 16, 2009 5:04 PM by bart

Hi Jarrett,

You're right about that; what's new is the context in which they're applicable/overloaded. Same deal with "where" and "in" in LINQ query comprehensions.

For what the meaning is concerned, conceptually they're a lot like foreach's "in" and output parameter passing's "out", and they really indicate the key thing developers need to be aware of when dealing with co/contravariance: restrictions on use sites for the generic parameter tagged as such.

So, it's fair to say the concept is somewhat similar, but obviously in terms of implementation they are very different.

Why just "somewhat" similar? Well, because the use of "in" in foreach is exactly opposite to the variance annotation on the thing you're enumerating over: an IEnumerator<T> is covariant in T ("out" keyword). But if you think of it, in the context of the definition and the caller they align properly: what's the output to the IEnumerator<T> becomes the input to the loop.

The situation with "out" is more odd - the meaning is the same: something is (restricted to) being passed out to the outside world, but output parameters and co/contravariance do not play together.

In and out were chosen precisely to make them feel natural at the declaration site; other choices would have been new keywords, some kind of attributes, wildcard syntax, etc. See for a great overview on options that were considered.

All of this said, you shouldn't worry too much about the declaration of those guys. 99% of the feature is to enable proper use of types that ought to be treated as covariant or contravariant (no need for call-site syntax at all); the remaining 1% is for people who might have an interface or delegate that's totally different from our interfaces or Func/Action delegates.


# Topics about Tour-eiffel | C# 4.0 Feature Focus ??? Part 4 ??? Co- and Contra-Variance for Generic&#8230;

Pingback from  Topics about Tour-eiffel |   C# 4.0 Feature Focus ??? Part 4 ??? Co- and Contra-Variance for Generic&#8230;

# Learning .NET 4.0 new features &laquo; Bogdan Brinzarea&#8217;s blog

Pingback from  Learning .NET 4.0 new features &laquo;  Bogdan Brinzarea&#8217;s blog

# Top 9 Posts from 2009

Sunday, January 03, 2010 1:15 AM by B# .NET Blog

select top 9 [Subject] from dbo . cs_Posts where postlevel = 1 and usertime &lt; &#39;01/01/2010&#39;

# Learning resources for C# 4.0 and .NET 4.0 new features | Noman Ali Blog

Pingback from  Learning resources for C# 4.0 and .NET 4.0 new features | Noman Ali Blog

# Covariance and Contravariance in .NET and Java &#8211; Part I &laquo; Johannes Rudolph&#039;s Blog

Pingback from  Covariance and Contravariance in .NET and Java &#8211; Part I &laquo; Johannes Rudolph&#039;s Blog

# Casting Generics - Programmers Goodies

Wednesday, July 27, 2011 12:05 PM by Casting Generics - Programmers Goodies

Pingback from  Casting Generics - Programmers Goodies

# Covariance and Contravariance made simple &laquo; Jack Watts&#039; Blog

Pingback from  Covariance and Contravariance made simple &laquo;  Jack Watts&#039; Blog