C#不可分配类型 - 泛型

时间:2016-06-26 05:13:04

标签: c# generics polymorphism

所以我只是在处理我正在处理的状态机类型,并且大多想要尝试使用Activator.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>();

它工作正常,但我想使用new()的继承类型,所以我可以确定我可以实例化它们。

我再次道歉,如果我做了一件令人难以置信的错事,我只是碰到了一堵砖墙而且我的谷歌搜索引导我走向了不好的方向。我没有包含任何其他接口或类,因为它们似乎不是问题的一部分,但如果需要我可以附加它们。谢谢你的帮助!

2 个答案:

答案 0 :(得分:4)

出现此问题是因为:

  1. ITransitionContainer与其类型参数不是covariant interface
  2. AddTransition方法通用参数not constrainedreference types
  3. _transitions不是具有ITransitionContainer值的字典,因此如果不将其更改为Dictionary<Type, ITransitionContainer<ITransition, IState>>,我们仍然无法添加正确的resticted协变转换。
  4. 简化示例

    考虑以下简化案例:

    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可以是结构和共同/(反)方差doesn't work with value types

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

    但是如果你制作那个方法constrained to only reference types,那么它在两种情况下都会起作用:

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

    将相同的原则(outclass)应用于您的两个通用参数,它将完成这项工作。

    针对您具体案例的完整解决方案:

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

答案 1 :(得分:2)

失败是因为泛型不是协变的。这个问题可以在这里看到:

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

就是这样。 :)

当然,你必须复制它所需要的一切,在这种情况下,似乎这两个对象就足够了。