C#, Tech

<in T> vs <out T> – Contravariance vs Covariance – (the easier) part 1.

Have you ever seen these mysterious ‘in’ and ‘out’ keywords in generic interfaces’ definitions? If so, you’ve probably already checked what it means, but if not… You better read this and the next post (I promise, I will write it soon, not like always :P). So today we will talk about the basics of variance in our own generic interfaces.

All you have to know!

In everyday programming, the most important rules are the two below:

interface ISomeName<in T> <– means that T can be only passed as a parameter to a method (it enters inteface’s methods, so it goes inside, maybe that’s why we use here keyword ‘in’… Hmm, no, that’s just a coincidence ?)

interface ISomeName <out T> <– means that T can be only returned as method results (it is what we receive from a method so it goes out of it – wow, again sounds legit!)

Great, you know now everything, no need to read the rest of this article :P. But if you stop reading now, my bounce rate will be high, so don’t do that! I will add a picture of a cat at the very end, I promise!

Contravariance (<in T>) – Example!

interface IBuildingManager<in T>
{
   void Clean(T item);
   void Add(T item);
   //T Sell(); // would cause an error!
}

In the above example, interface was marked as contravariant, so its generic T type can only enter IBuildingManager’s methods (and we cannot use it as a return type).

If we uncomment the last method in the interface, we will end up with the compiler error:

Invalid variance: The type parameter ‘T’ must be covariantly valid on ‘IBuildingManager<T>.Sell()’. ‘T’ is contravariant.

Covariance (<out T>) – Example!

interface ICarManager<out T>
{
   T GetByName(string name);
   //void Add(T item); // would cause an error!
}

Again, we have an interface with specified variance – this time covariance. Which of course means, type T can only be returned by ICarManager’s methods. When we try to pass object of type T as a parameter to any method in this interface, we will get the following compilation error:

Invalid variance: The type parameter ‘T’ must be contravariantly valid on ‘ICarManager<T>.Add(T)’. ‘T’ is covariant.

Why not both?

Of course, there is no problem if you want your interface to have two (or more) generic types – one covariant and the other contracovariant. In this scenario, you just add a magic keywords before types names:

interface IGarageManager<out T, in Z>
{
   T GetGarageContent(string garageName);
   void Add(Z item);
} 

And the example class implementing the above interface:

public class StandardGarageManager : IGarageManager<Garage, Car>
{
   public void Add(Car item)
   {
      //…
   }

   public Garage GetGarageContent(string name)
   {
     //…
   }
}

Variance (<T>) – Example!

Of course, generic type in interface can be both covariant and contravariant. And that’s what we usually meet in projects ?. Let’s emphasize – the below types can be passed to a method and be returned from it:

interface IMechanic<G, M>
{
   G GetGarageOfMechanic(M mechanic);
   void HireMechanicInGarage(M mechanic, G garage);
}

Summary

As you can see, when writing our own generic interfaces we can control how generic types will be used. Sometimes we expect a generic type to specify a method parameter, sometimes we want it to be a return type. But in fact, it’s not all we get from c# variances… So stay tuned, the next post will appear soon ;).

PS Of course I didn’t forget – a cat picture I promised! (photo by Thomas Le on Unsplash)!

Featured photo by Cyril Saulnier on Unsplash

Share this: