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 ответов
эта проблема возникает потому, что:
-
ITransitionContainer
- это не ковариантные интерфейс над его аргументами типа. -
AddTransition
общие аргументы метода являются не ограничено на ссылка типа. -
_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 };
}
и это все. :)
конечно, вам придется скопировать все необходимое для его работы, в этом случае кажется, что этих двух объектов достаточно.