What you can do with "Covariant"?
Covariant uses the modifier out, meaning that the type can be an output of a method, but not an input parameter. 
Suppose you have these class and interface:
interface ICanOutput<out T> { T getAnInstance(); }
class Outputter<T> : ICanOutput<T>
{
    public T getAnInstance() { return someTInstance; }
}
Now suppose you have the types TBig inheiriting TSmall. This means that a TBig instance is always a TSmall instance too; but a TSmall instance is not always a TBig instance. (The names were chosen to be easy to visualize TSmall fitting inside TBig)
When you do this (a classic covariant assignment):
//a real instance that outputs TBig
Outputter<TBig> bigOutputter = new Outputter<TBig>();
//just a view of bigOutputter
ICanOutput<TSmall> smallOutputter = bigOutputter;
bigOutputter.getAnInstance() will return a TBig  
- And because 
smallOutputter was assigned with bigOutputter:
- internally, 
smallOutputter.getAnInstance() will return TBig  
- And 
TBig can be converted to TSmall  
- the conversion is done and the output is 
TSmall.     
 
If it was the contrary (as if it were contravariant):
//a real instance that outputs TSmall
Outputter<TSmall> smallOutputter = new Outputter<TSmall>();
//just a view of smallOutputter
ICanOutput<TBig> bigOutputter = smallOutputter;
smallOutputter.getAnInstance() will return TSmall  
- And because 
bigOutputter was assigned with smallOutputter:
- internally, 
bigOutputter.getAnInstance() will return TSmall  
- But 
TSmall cannot be converted to TBig!! 
- This then is not possible. 
 
 
This is why "contravariant" types cannot be used as output types    
What you can do with "Contravariant"?
Following the same idea above, contravariant uses the modifier in, meaning that the type can be an input parameter of a method, but not an output parameter.
Suppose you have these class and interface:
interface ICanInput<in T> { bool isInstanceCool(T instance); }
class Analyser<T> : ICanInput<T>
{
    bool isInstanceCool(T instance) { return instance.amICool(); }
}
Again, suppose the types TBig inheriting TSmall. This means that TBig can do everything that TSmall does (it has all TSmall members and more). But TSmall cannot do everything TBig does (TBig has more members). 
When you do this (a classic contravariant assignment):
//a real instance that can use TSmall methods
Analyser<TSmall> smallAnalyser = new Analyser<TSmall>();
    //this means that TSmall implements amICool
//just a view of smallAnalyser
ICanInput<TBig> bigAnalyser = smallAnalyser;
smallAnalyser.isInstanceCool:
smallAnalyser.isInstanceCool(smallInstance) can use the methods in smallInstance  
smallAnalyser.isInstanceCool(bigInstance) can also use the methods (it's looking only at the TSmall part of TBig) 
 
- And since 
bigAnalyser was assigned with smallAnalyer:    
- it's totally ok to call 
bigAnalyser.isInstanceCool(bigInstance)  
 
If it was the contrary (as if it were covariant): 
//a real instance that can use TBig methods
Analyser<TBig> bigAnalyser = new Analyser<TBig>();
    //this means that TBig has amICool, but not necessarily that TSmall has it    
//just a view of bigAnalyser
ICanInput<TSmall> smallAnalyser = bigAnalyser;
- For 
bigAnalyser.isInstanceCool:
bigAnalyser.isInstanceCool(bigInstance) can use the methods in bigInstance  
- but 
bigAnalyser.isInstanceCool(smallInstance) cannot find TBig methods in TSmall!!! And it's not guaranteed that this smallInstance is even a TBig converted.        
 
- And since 
smallAnalyser was assigned with bigAnalyser:
- calling 
smallAnalyser.isInstanceCool(smallInstance) will try to find TBig methods in the instance     
- and it may not find the 
TBig methods, because this smallInstance may not be a TBig instance.     
 
This is why "covariant" types cannot be used as input parameters
Joining both
Now, what happens when you add two "cannots" together? 
- Cannot this + cannot that = cannot anything    
 
What could you do?
I haven't tested this (yet... I'm thinking if I'll have a reason to do this), but it seems to be ok, provided you know you will have some limitations.
If you have a clear separation of the methods that only output the desired type and methods that only take it as an input parameter, you can implement your class with two interfaces. 
- One interface using 
in and having only methods that don't output T  
- Another interface using 
out having only methods that don't take T as input 
Use each interface at the required situation, but don't try to assign one to another.