将泛型对象添加到泛型集合时编译时间错误

时间:2013-01-18 15:54:58

标签: c# generics

我们已经达到了一个我们不知道如何继续的地步:

SHORT: 我们有一个通用接口和一个通用接口的集合。尝试将通用接口的实现添加到集合失败。发生的事情是我得到一个编译时异常说:

  

无法转换为TestApp.IState< T>'到TestApp.IState< TestApp.IView>'

LONG [代码示例]:

class Program
{
    static void Main(string[] args)
    {
        var coll = new StateCollection();
        var state = new SomeState();
        coll.AddState(state);
    }
}

public class StateCollection
{
    private List<StateBase<IView>> _states = new List<StateBase<IView>>();

    public void AddState<T>(StateBase<T> state) where T: IView
    {
        _states.Add(state);
    }
}

public class SomeState : StateBase<SomeView>
{

    public IView View
    {
        get;
    }
}

public class SomeView : IView
{
}

public abstract class StateBase<T> where T : IView
{
    private SomeView _view;
    public SomeView View
    {
        get { return _view; }
    }
}

public interface IView
{
}

为什么会这样?在AddState中我们提到T必须是IState的一个实例。有人可以帮助我们解决为什么会发生这种情况以及如何做我们想做的事情吗?

EDIT1: 我们也尝试过:

    public void AddState(IState<IView> state)
    {
        _states.Add(state);
    }

但是这只会将编译时错误移到'coll.AddState(state)' 所以同样的事情发生在另一个地方。

EDIT2: 问题!我没有给出正确的例子。 IState不是一个接口而是一个抽象类。非常抱歉!更改了代码以使用抽象类

5 个答案:

答案 0 :(得分:2)

第一个解决方案

public class StateCollection
    {
        private readonly List<IState<IView>> _states = new List<IState<IView>>();

        public void AddState(IState<IView> state)
        {
            _states.Add(state);
        }
    }
如同Nevyn所建议的那样。 要让它发挥作用,请在T

中将interface IState<T>标记为协变
public interface IState<out T> where T:IView
    {
        IView View { get; }
    }

第二个解决方案 :(在课程StateCollection中保持更改)

将界面IState<T>更改为

public interface IState<out T> where T : IView
    {
        T View { get; }
    }

并将SomeState类转换为

public class SomeState : IState<SomeView>
    {
        public SomeView View{ get;private set; }
    }

Edit2的解决方案:

class Program
    {
        static void Main(string[] args)
        {
            var coll = new StateCollection();
            var state = new SomeState();
            coll.AddState(state);
            Console.ReadKey();
        }
    }

    public class StateCollection
    {
        private List<IStateBase<IView>> _states = new List<IStateBase<IView>>();

        public void AddState(IStateBase<IView> state)
        {
            _states.Add(state);
        }
    }

    public class SomeState : StateBase<SomeView>
    {
    }

    public class SomeView : IView
    {
    }

    public interface IStateBase<out T> where T : IView
    {
        T View { get; }
    }

    public abstract class StateBase<T> : IStateBase<T> where T : IView
    {
        public T View { get; set; }
    }

    public interface IView
    {
    }

答案 1 :(得分:2)

这些变化如何:

public class SomeState : IState<SomeView>
{
    public SomeView View
    {
        get;
        set;
    }
} 

而不是使用泛型Type T而不是在AddState方法中使用IView

public void AddState(IState<IView> state)
{
    _states.Add(state);
}

使用out关键字

在IState中创建T协变
public interface IState<out T> where T : IView
{
    T View { get; }
}

EDIT2的解决方案:

不知道你是否可以,但你可以。

public class StateCollection
{
    private List<IState<IView>> _states = new List<IState<IView>>();

    public void AddState(IState<IView> state)
    {
        _states.Add(state);
    }
}

public class SomeState : StateBase<SomeView>
{
    public override SomeView View
    {
        get { return null; }
    }
}

public abstract class StateBase<T> : IState<T> where T : IView
{
   public abstract T View { get; }
}

public interface IState<out T> where T : IView
{
    T View { get; }
}

答案 2 :(得分:1)

这看起来更像是Add函数的参数错误。您是否尝试过在不使用泛型的情况下声明添加功能?继承本身应该允许它。使AddState函数看起来像这样:

编辑(根据Edit2):

如上所述,继承本身应该处理泛型。只要您声明的任何类正确实现IViewIState<IView>,那么就不应该有任何问题...

public absract class StateBase
{
    public IView view { get; set; }

    ....
}

public Interface IView
{ ... }

public class StateCollection
{
    private List<StateBase> _states = new List<StateBase>();

    public void AddState(StateBase state)
    {
        _states.Add(state);
    }
}

public class SomeView : IView
{ ... }

等等等,根据需要经常使用

public class SomeState : StateBase
{
    private SomeView my_view;

    public IView view
    {
        get { return (IView)SomeView; }
        set { ; }
    }
}

//program remains unchanged

在这种情况下,SomeState仍然是IState对象,所有IState对象都实现IView,而SomeView是IView对象。 SomeState在内部实现SomeView。对我来说看起来一样,但我不知道适应性对你的真实代码有多好。

任何其他类都将遵循相同的模型。 State将实现StateBase,并在内部声明一个自定义View,它本身需要扩展IView。这样,在自定义视图上投射的IView就可以了。

来自评论:

public class BarState : StateBase
{
    private BarView my_view;

    public IView view
    {
        get { return (IView)BarView; }
        set { ; }
    }
}

public class BarView : IView
{ ... }

答案 3 :(得分:0)

添加接口IState(非通用)并从IState<T>继承。然后将_states声明为List<IState>和方法AddState(IState state)

答案 4 :(得分:0)

(编辑:你只需要让你的StateBase从协变接口继承。你不能让一个类直接协变,你总是需要通过一个接口)

试试这个:

public class StateCollection
{
    private List<IState<IView>> _states = new List<IState<IView>>();

    public void AddState(IState<IView> state)   
    {
        _states.Add(state);
    }
}

public class SomeState : StateBase<SomeView>
{
}

public class SomeView : IView
{
}

public interface IState<out T> where T : IView // now covariant with T
{
    T View { get; }
}

public abstract class StateBase<T> : IState<T> where T : IView
{
    public T View { get; set; }
}

public interface IView
{
}