C# Не Присваивается Тип - Дженерики

так что я просто взламываю с типом государственной машины, над которой я работал, и в основном хочу просто попробовать активатор.CreateInstance метод, чтобы увидеть, как это было, и я столкнулся с проблемой, где я не могу использовать where статья, Как я думаю. Я заранее извиняюсь, если я просто идиот, и все смеются надо мной. Поэтому у меня есть 2 небольших класса.

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, 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);
    }

на строку _transitions.Add(typeof(TTransition), transitionContainer); Я получил cannot convert TransitionContainer<TTransition,TStateTo> expression to type TransitionContainer<ITransition,IState> ошибка.

если я изменю общие параметры на

var transitionContainer= new TransitionContainer<ITransition, IState>();

он отлично работает, но я хотел использовать унаследованные типы, которые являются новыми (), чтобы я мог быть уверен, что я мог инстанцировать их.

еще раз прошу прощения, если я делаю что-то невероятно неправильно, я просто наткнулся на кирпичную стену, и мой гуглинг привел меня в плохом направлении. Я не включил ни один из других интерфейсов или классов, поскольку они не казались частью проблемы, но при необходимости я могу прикрепить их. Спасибо за любую помощь!

2 ответов


эта проблема возникает потому, что:

  1. ITransitionContainer - это не ковариантные интерфейс над его аргументами типа.
  2. AddTransition общие аргументы метода являются не ограничено на ссылка типа.
  3. _transitions это не словарь с ITransitionContainer значения, поэтому без изменения его на Dictionary<Type, ITransitionContainer<ITransition, IState>> мы все равно не сможем добавить даже правильно д ковариантные transtions.
упрощенный

рассмотрим следующий упрощенный случай:

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();
        }
    }
}

он потерпит неудачу в обоих

public static void Do<TTransition>()
    where TTransition : ITransition
{
    ITest<ITransition> item = new SomeTest<TTransition>();
}

и

ITest<ITransition> item = new SomeTest<SomeTransition>();

если вы ITest ковариантные

public interface ITest<out TTransition>

, тогда он потерпит неудачу только в общем методе. Потому что здесь TTransition может быть структурой и дисперсией co/(contra)не работает с типами значений:

public static void Do<TTransition>()
    where TTransition : ITransition
{
    ITest<ITransition> item = new SomeTest<TTransition>();
}

но если вы сделаете это метод ограничено только ссылочными типами, тогда он будет работать в обоих случаях:

public static void Do<TTransition>()
    where TTransition : class, ITransition
{
    ITest<ITransition> item = new SomeTest<TTransition>();
}

применить тот же принцип(out и class) к вашим двум общим Аргументам, и он выполнит эту работу.


полное решение для вашего конкретного случая:

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);
    }
}

это не удается, потому что дженерики не ковариантны. Проблему можно увидеть здесь:

TransitionContainer<ITransition, IState> value = new TransitionContainer<TTransition, TStateTo>();

это дает вам ту же ошибку. Вы также получите эту ошибку примерно так:

List<IComparable> list = new List<DateTime>();

Visual Studio говорит вам (в основном), что:

Cannot implicitly convert type 'List<System.DateTime>' to 'List<System.IComparable>'

что вам нужно сделать, это преобразовать объект. Вы можете создать Convert метод, который возвращает TransitionContainer<ITransition, IState> и затем использовать .Add(typeof(TTransition), transitionContainer.Convert()) (или как вы назвали его).

но самый безболезненный вариант создайте неявное преобразование для вашего TransitionContainer<TTransition, TStateTo> объект, добавив этот статический метод:

public static implicit operator TransitionContainer<ITransition, IState>(TransitionContainer<TTransition, TStateTo> value)
{
    return new TransitionContainer<ITransition, IState>() { StateTo = value.StateTo, Transition = value.Transition };
}

и это все. :)

конечно, вам придется скопировать все необходимое для его работы, в этом случае кажется, что этих двух объектов достаточно.