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 :)