Home > other >  Generic nested classes confusion
Generic nested classes confusion

Time:10-06

I'm working with some complex generics to allow inheritance from root class with state information and I don't quite understand two different scenarios (from my perspective it's the same, but probably I'm wrong):

Why this is ok:

    class Client
    {
        public interface IState { }

        protected class State : IState { }

        class States<T> where T : IState
        {
            public States(T state) { }
        }

        public Client()
        {
            State state = new State();
            States<IState> states = new States<IState>(state);
        }
    }

But this is not:

    class Client<T> where T : Client<T>.IState
    {
        public interface IState { }

        protected class State : IState { }

        class States
        {
            public States(T state) { }
        }


        public Client()
        {
            State state = new State();
            States states = new States(state); //Error -> Can't convert Client<T>.State to T
        }
    }

CodePudding user response:

The code reported here is quite convoluted and difficult to understand. I would suggest, if possible, to change it and avoid such a complex nesting of types.

That said, let's try to understand why your code is not compiling.

First of all, let's notice that the comment reported in your question is wrong. The error preventing to compile is that there is no way to convert from Client<T>.State to the generic type parameter T. Notice that the state variabile has a compile time type of Client<T>.State, but the constructor of the States class requests a parameter of type T.

The only thing you know about the T type is that it implements the Client<T>.IState interface. The Client<T>.State class also implements the Client<T>.IState interface, but this is not enough for an implicit conversion between Client<T>.State and T to exist.

To generalize: having two types implementing the same interface does not imply a conversion between them to exist.

As an example, consider the following code.

public interface IFoo {}

public class Foo: IFoo {}

public class Bar: IFoo {}

both Foo and Bar implements the IFoo interface, but there is no way to convert between Foo and Bar.

CodePudding user response:

class States
{
    public States(T state) { }
}

Has no idea what T is. It registers as a CS0246 error, which sounds weird but is the true issue at stake here, the compiler doesn't know that you mean T the generic, and not a T class that you may or may not have created yourself

Generic T is not an operator on its own, it needs the <T> for the compiler to know that you intend to use type parameters

You must annotate the class (since this is a constructor) or any other methods to declare upfront it uses generics

class States<T> where T : SomeConstraint
{
    public States(T state) { }
}

CodePudding user response:

Consider the following:

class NotAState : Client<NotAState>.IState {}
class Client<T> where T : Client<T>.IState
{
    public interface IState { }

    protected class State : IState { }

    class States
    {
        public States(T state) { }
    }


    public Client()
    {
        State state = new State();
        States states = new States(state); //Error -> Can't convert Client<T>.State to T
    }
}

var client = new Client<NotAState>();

Here, States must be constructed by passing an instance of NotAState, yet you pass in a State. A state is not a notastate :)

  •  Tags:  
  • c#
  • Related