Search code examples
c#genericsinheritanceencapsulationgeneric-programming

CS0311 Error when extending generic types in class headers


CS0311: The type 'type1' cannot be used as type parameter 'T' in the generic type or method ''. There is no implicit reference conversion from 'type1' to 'type2'.

I have these two classes to start off with:

public abstract class Node<N> where N : Node<N> { ... }
public class Pathfinder<N> where N : Node<N> { ... }

Then, I have an implementation of Node called Node2D that takes a different generic type, which must extend another class called World.

public abstract class World<W> where W : World<W> { ... }
public class Node2D<W> : Node<Node2D<W>> where W : World<W> { ... }

I can create an object of type Pathfinder< Node2D< TestWorld > > without any issue (see below). However, when I try to create a Pathfinder using types that extend Node2D< TestWorld >, VSC gives me a CS0311 error. This happens when I make the following specific classes:

public class TestWorld : World<TestWorld> { ... }
public class TestNode : Node2D<TestWorld> { ... }

// inside some function
Pathfinder<TestNode> pathfinder = new Pathfinder<TestNode>(); // gives CS0311 error where type1 is TestNode, and type2 is Node<TestNode>

And it also happens when I make a class to contain the specific implementation of Pathfinder

public class Pathfinder2D<X, W> where X : Node2D<W> where W : World<W> {

    private Pathfinder<X> _pathfinder; // gives CS0311 error where type1 is X, type2 is Node<X>

}

How come the original Pathfinder will work for type Node2D< W > for any W, but not for specific implementations of Node2D< W >? If no extensions of generic types were allowed at all, I thought that Node2D< W > would also not work and one could only use the original Node< N > as a type for Pathfinder< N >. Since that worked, I then thought that so long as I have an implementation of Node2D with a specific W, that should work just as well with Pathfinder< N >. If Pathfinder< Node2D< TestWorld > > works, why doesn't Pathfinder< TestNode > where TestNode is Node2D< TestWorld >?


Solution

  • CS0311 states that in this declaration

    Pathfinder<TestNode> p;
    

    TestNode must be convertible to Node<TestNode>. That's what the constraint for Pathfinder<T>'s generic argument states:

    public class Pathfinder<N> where N : Node<N>
    

    But TestNode is derived from Node2D<Testworld>. And according to the declaration of Node2D<T>

    public class Node2D<W> : Node<Node2D<W>>
    

    we know that TestNode is actually a Node<Node2D<Testworld>>. And that is not a Node<TestNode>.

    So your generic declarations and constraints are contradictionary.

    This seems to be a design flaw and how to solve this depends on what you are trying to achieve.