And now for Contravariance

Take the classes in the last post (First, Second, Third) and assume they are exactly the same in this example.

In Covariance, we learned that whatever variable to you set equal to the return of a method has to be equal in type of larger. Or in other words, it has to have equal or less functionality.

  Second second = ReturnThird();  //OK since second has less functionality

  Third third = ReturnSecond(); //BAD since third has more functionality

Now I think you can guess what Contravariance is, but if you can’t it’s ok. Most likely you’re a tool just like me. Contravariance is the movement from small to large meaning that the type must be equal to or larger than. Following the “in other words” manor, it has to have equal or more functionality.

Now small note before I go on, saying that it has to have more/less functionality can be somewhat dangerous. After all, Third could inherit from Second and add no functionality, but I find this is an easier way to think of it. I suppose another way of thinking of it is that with Covariance the return type has to have equal or more knowledge. Meaning, Second has full knowledge of what First is, but Second has no idea what Third is.

Anywho, onto some examples. Say we take the FillX methods and add something to it.

  private void FillFirst(First firstToFill)
  {
    firstToFill.FirstOutput = "";
  }

  private void FillSecond(Second secondToFill)
  {
    secondToFill.SecondOutput = "";
  }

  private void FillThird(Third thirdToFill)
  {
    thirdToFill.ThirdOutput = "";
  }

Right off the bat you might notice that if methods allowed Covariance with parameters, you’d be in trouble. After all, if FillThird allowed parameter covariance, you could pass in a First object. What what that object do with ThirdOutPut? As things are, you would have a bad day. Lucky for you, at least if you aren’t adamant about wanting Covariance in parameters, this can’t happen.

Well shoot, I just gave away the fun of this post. Oh well, I’ll keep going in case you just have more time.

  Action<First> fillFirstAction = FillFirst;
  //No problems here since FillFirst expects a First

  Action<Second> fillSecondAction = FillFirst;
  //Still no problems although this may look odd.  But remember, FillFirst
  //just needs an object that : First, it doesn't care if the object
  //has more functionality than first.
  //
  //The FillFirst method uses the FirstOutput property and by inheritance
  //the Second being passed in has said property

  Action<Second> fillThirdAction = FillThird;
  //Not gonna happen.  The FillThird expects a third or smaller object.  Since
  //Third : Second, third is smaller than second.  Implications?  Look in the 
  //FillThirdMethod
  //
  //The method expects the object to have the ThirdOutput property which means
  //Second has to inherit from Third.  We know this to be untrue.

So basically Contravariance is used with parameters in methods to guarantee the object being passed in has at least the functionality used within the method.

Apparently there was a problem in the lobby so there will be no refreshments served tonight.

Covariance versus Contravariance

Ok so I stumbled on to this subject the other day and thought it was worth noting. Take these simple classes:

public class First
{
  public String FirstOutput { get; set; }
}

public class Second : First
{
  public String SecondOutput { get; set; }
}

public class Third : Second
{
  public String ThirdOutput { get; set; }
}

So from this you can see that Third inherits Second which in turns inherits First. By terminology this would mean that Third is “smaller” than Second and First is “larger” than both. Here’s an example of Covariance:

public class Covariance
{
  public Covariance()
  {

      Func<First> returnFirstFunc = ReturnFirst;
      //This works since the Func has a return type of First

      Func<Second> returnSecondFunc = ReturnThird;
      Second secondTest = returnSecondFunc();
      secondTest.FirstOutput = "First";
      secondTest.SecondOutput = "First";
      //This works since the Func has a return type of Third which is smaller
       //that Second.  Therefore anyone using this Func will expect a Second to
       //be returned and will only use the methods/properties that a Second object
       //would have.  Methods/Properties that Third has by inheritance.

      Func<Third> returnThirdFunc = ReturnSecond;
      //THIS WILL NOT WORK
       //Due to Covariance, the return of the method must be equal or smaller
       //that the expected type.  returnThirdFunc expects a Third or smaller object
       //but the ReturnSecond method returns a Second which is not smaller than Third.
       //Afterall, Third : Second
       //
       //Third thirdTest = returnThirdFunc();
       //Is the same as:
       //Third thirdTest = new Second();
  }

  private First ReturnFirst()
  {
      return new First();
  }

  private Second ReturnSecond()
  {
      return new Second();
  }

  private Third ReturnThird()
  {
      return new Third();
  }
}

Basically what this all means is that with return types, the return type must be smaller or equal to the field it’s being set to. When you are dealing with Funcs, the return type must be smaller or equal to the return type for the method it’s being set it. Why is that? Well think of it like this:

It’s your first day on the job and some guy tells you to write something with whatever returnFirstFunc() returns. Now you have no way to look at the code, so you can only know that it returns First. For all you know, it could return First, Second, or Third. So you would do this:

First someFirst;

someFirst = returnFirstFunc();  //Could return anything smaller than First
someFirst.FirstOutput;  //Completely legal and safe

But would you do this?

someFirst.ThirdOutput;

Of course not since you only can assume it is a First. Now let’s do this in reverse. Say from the above example you were allowed to do this:

Func<Third> returnThirdFunc = ReturnSecond;

Could you do this?

Third third;

third = returnThirdFunc();
third.ThirdOutput;

Yeah you can’t since the Second type doesn’t have the ThirdOutput property.

In short Covariance is the allowance of Smaller types or equal. If a method returns back Third, then you can use that method for anything that is Third or Smaller (Second, First, Object) but not for something Larger (Fourth, Fifth, ect).