C#不可分配类型 - 泛型

Tra*_*ott 6 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; }
}
Run Code Online (Sandbox Code Playgroud)

以及

  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);
    }
Run Code Online (Sandbox Code Playgroud)

所以在线上_transitions.Add(typeof(TTransition), transitionContainer);我收到一个cannot convert TransitionContainer<TTransition,TStateTo> expression to type TransitionContainer<ITransition,IState>错误.

如果我将通用参数更改为

var transitionContainer= new TransitionContainer<ITransition, IState>();
Run Code Online (Sandbox Code Playgroud)

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

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

Eug*_*kal 4

出现此问题的原因是:

  1. ITransitionContainer不是其类型参数的协变接口。
  2. AddTransition方法泛型参数不限引用类型
  3. _transitions不是带有ITransitionContainer值的字典,因此如果不将其更改为,Dictionary<Type, ITransitionContainer<ITransition, IState>>我们仍然无法添加正确限制的协变转换。

简化示例

考虑以下简化情况:

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

两者都会失败

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

ITest<ITransition> item = new SomeTest<SomeTransition>();
Run Code Online (Sandbox Code Playgroud)

如果你做ITest协变

public interface ITest<out TTransition>
Run Code Online (Sandbox Code Playgroud)

,那么它只会在通用方法中失败。因为这里TTransition可以是一个结构体,而 co/(contra)variance不适用于值类型

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

但是,如果您将该方法限制为仅引用类型,那么它在两种情况下都可以工作:

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

将相同的原则(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);
    }
}
Run Code Online (Sandbox Code Playgroud)