So I'm just hacking around with a state machine type I was working on and mostly wanting to just try out the Activator.CreateInstance method to see what it was like, and I ran into a problem where I cant seem to use the where clause as I would think.  I apologize ahead of time if I am just an idiot and everyone laughs me out of here.  So I have 2 small classes.
public class TransitionContainer<TTransition, TStateTo> :
    ITransitionContainer<TTransition, TStateTo>
    where TTransition : ITransition
    where TStateTo : IState
{
    public TransitionContainer()
    {
        StateTo = typeof(TStateTo);
        Transition = Activator.CreateInstance<TTransition>();
    }
    public Type StateTo { get; private set; }
    public TTransition Transition { get; private set; }
}
as well as
  public class StateContainer<T> : IStateContainer<T> where T : IState
{
    private Dictionary<Type, TransitionContainer<ITransition, IState>> _transitions =
        new Dictionary<Type, TransitionContainer<ITransition, IState>>();
    public StateContainer()
    {
        State = Activator.CreateInstance<T>();
    }
    public T State { get; private set; }
    public int TransitionCount
    {
        get { return _transitions.Count; }
    }
    public void AddTransition<TTransition, TStateTo>() where TTransition : ITransition, new()
        where TStateTo : IState, new()
    {
        var transitionContainer= new TransitionContainer<TTransition, TStateTo>();
        _transitions.Add(typeof(TTransition), transitionContainer);
    }
So on the line _transitions.Add(typeof(TTransition), transitionContainer); I receive a cannot convert TransitionContainer<TTransition,TStateTo> expression to type TransitionContainer<ITransition,IState> error.
If I change the generic parameters to
var transitionContainer= new TransitionContainer<ITransition, IState>();
it works fine, but I wanted to use inherited types that are new() so I could be sure I could instantiate them.
Again I apologize if I'm doing something incredibly wrong, I was just kind of ran into a brick wall and my googling led me in no good direction. I didnt include any of the other interfaces or classes as they didn't seem to be part of the problem, but if there needed I can attach them. Thanks for any help!
This issue happens because:
ITransitionContainer is not a covariant interface over its type arguments. AddTransition method generic arguments are not constrained to be reference types._transitions is not a dictionary with ITransitionContainer values, so without changing it to Dictionary<Type, ITransitionContainer<ITransition, IState>> we still won't be able to add even properly resticted covariant transtions.Consider the following simplified case:
public interface ITransition
{
}
public class SomeTransition : ITransition
{
}
public interface ITest<TTransition>
    where TTransition : ITransition
{
    TTransition Value { get; }
}
public class SomeTest<TTransition> : ITest<TTransition>
    where TTransition : ITransition
{
    public TTransition Value
    {
        get
        {
            throw new NotImplementedException();
        }
    }
}
It will fail in both
public static void Do<TTransition>()
    where TTransition : ITransition
{
    ITest<ITransition> item = new SomeTest<TTransition>();
}
and
ITest<ITransition> item = new SomeTest<SomeTransition>();
If you make ITest covariant
public interface ITest<out TTransition>
, then it will fail only in generic method. Because here TTransition can be a struct and co/(contra)variance doesn't work with value types:
public static void Do<TTransition>()
    where TTransition : ITransition
{
    ITest<ITransition> item = new SomeTest<TTransition>();
}
But if you make that method constrained to only reference types, then it will work in both cases:
public static void Do<TTransition>()
    where TTransition : class, ITransition
{
    ITest<ITransition> item = new SomeTest<TTransition>();
}
Apply the same principle(out and class) to your two generic arguments and it will do the job.
public interface IState
{    }
public interface ITransition
{    }
// !!!!! - Here we add out specifier
public interface ITransitionContainer<out TTransition, out TStateTo>
    where TTransition : ITransition
    where TStateTo : IState
{
    Type StateTo
    {
        get;
    }
    TTransition Transition
    {
        get;
    }
}
public interface IStateContainer<T> where T : IState
{
    T State
    {
        get;
    }
}
public class TransitionContainer<TTransition, TStateTo> : ITransitionContainer<TTransition, TStateTo>
    where TTransition : ITransition
    where TStateTo : IState
{
    public TransitionContainer()
    {
        StateTo = typeof(TStateTo);
        Transition = Activator.CreateInstance<TTransition>();
    }
    public Type StateTo { get; private set; }
    public TTransition Transition { get; private set; }
}
public class StateContainer<T> : IStateContainer<T> where T : IState
{
    private Dictionary<Type, ITransitionContainer<ITransition, IState>> _transitions =
        new Dictionary<Type, ITransitionContainer<ITransition, IState>>();
    public StateContainer()
    {
        State = Activator.CreateInstance<T>();
    }
    public T State { get; private set; }
    public int TransitionCount
    {
        get { return _transitions.Count; }
    }
    public void AddTransition<TTransition, TStateTo>()
        // !!!!!! - Here we add class constraints
        where TTransition : class, ITransition, new()
        where TStateTo : class, IState, new()
    {
        var transitionContainer = new TransitionContainer<TTransition, TStateTo>();
        _transitions.Add(typeof(TTransition), transitionContainer);
    }
}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With