Search code examples
c#castingcovariancecontravariance

Contravariance/Covariance, why can't cast this?


Let's face it, I am still having some difficulties to understand the constrains when it's time to use covariance and contravariance in generics.

I wonder, why if I have this:

public interface IFasterListCov<out T>
{}

public interface IFasterListCon<in T>
{}

public class FasterList<T> : IList<T>, IFasterListCov<T>, IFasterListCon<T>

The third cast fails:

public void QueryNodes<T>() where T:INode
{
     //somehow I can convert IFasterListCon<INode> to IFasterListCon<T>
     IFasterListCon<INode> nodes = (IFasterListCon<INode>)_nodesDB[type];
     //I guess this works because _nodesDB[type] is indeed a FasterList<T> object
     //note: I am wrong, I can cast whatever INode implementation, not just T, which made me very confused :P
     IFasterListCon<T> nodesT = (IFasterListCon<T>)nodes; 
     //I can't cast IFasterListCon<T> back to FasterList<T>
     FasterList<T> nodeI = nodesT as FasterList<T>; //null
}

Dictionary<Type, IFasterListCov<INode>>  _nodesDB;

to be clear _nodesDB[type] is a FasterList<T> declared through IFasterListCov<INode>

Solution

  • MyType : IMyType does not make Generic<IMyType> and Generic<MyType> related in any way.

    In your particular case it is likely that nodesT is FasterList<Node> which is not FasterList<INode>.

    Note that this conversion work for interface which support variance (co/contra) when you can specify in/out as you see in successful conversion to interface. See one of many questions for details - i.e. Generic Class Covariance.

    There is also excellent answer about List co-variance - C# variance problem: Assigning List<Derived> as List<Base> which shows that List<Derived> and List<Base> can't be cast between each other:

    List<Giraffes> giraffes = new List<Giraffes>();
    List<Animals> animals = new List<Animals>() {new Lion()};
    
    (giraffes as List<Animals>).Add(new Lion()); // What? Lion added to Girafes
    Giraffe g = (animals as List<Giraffes>)[0] ; // What? Lion here?