【问题标题】:Compile time error when adding generic object to collection of generics将泛型对象添加到泛型集合时出现编译时错误
【发布时间】:2013-01-19 00:44:14
【问题描述】:

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

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

无法从 TestApp.IState' 转换为 TestApp.IState'

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 的一个实例。有人可以帮助我们了解为什么会发生这种情况以及如何做我们想做的事情吗?

编辑1: 我们也尝试过:

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

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

编辑2: 问题!我没有给出正确的例子。 Out IState 不是接口而是抽象类。对此非常抱歉!更改代码以使用抽象类

【问题讨论】:

  • 您使用的是什么版本的 C#? C#
  • 这是因为IState&lt;SomeView&gt; 不是IState&lt;IView&gt;&gt;
  • _states 被定义为 > 但您试图将 IState 添加到该集合中。
  • @sll SomeView 确实实现了 IView
  • @Oded 我使用的是 .NET 4.0 版

标签: c# generics


【解决方案1】:

这些变化怎么样:

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

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

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】:

第一个解决方案

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&lt;T&gt; 中的协变体

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

第二个解决方案:(在课堂上保持变化StateCollection)

将接口IState&lt;T&gt;改为

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

【讨论】:

  • @MrSoundless 那么...确实,它不起作用,因为您不能在接口和委托之外使用协方差。
  • @MrSoundless 请参阅“Edit2 的解决方案”以获得解决方案。
【解决方案3】:

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

编辑(根据 Edit2):

如前所述,继承本身应该处理泛型。只要您声明的任何类正确实现了IView 或IState&lt;IView&gt;,那么就不会有任何问题...

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
{ ... }

【讨论】:

  • 在您发布此问题的同时,我正在编辑帖子。这也行不通。当我这样做时,我只是在调用 AddState 时得到“'TestApp.StateCollection.AddState(TestApp.IState)' 的最佳重载方法匹配有一些无效参数”(参见 EDIT1)
  • 我尝试将 state 设置为 IState 但这只是返回 null ,转换失败
  • 如果转换失败,则意味着代码无法将 SomeState 识别为 IState 的正确实例,否则您将能够转换它。这似乎是问题的根源,继承并没有像模板一样正常工作。
  • 是的,这似乎是问题所在,但我们为什么以及如何解决这个问题?
  • 我现在真正的问题:只要假设 IView 和 StateBase 及其关系保持不变,删除模板会破坏任何其他现有代码吗?
【解决方案4】:

添加接口IState(非泛型)并从IState&lt;T&gt; 继承。然后将_states 声明为List&lt;IState&gt; 和方法AddState(IState state)

【讨论】:

    【解决方案5】:

    (编辑:你只需要让你的 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
    {
    }
    

    【讨论】:

    • @MrSoundless 看到编辑回答。如果您可以更改抽象类的定义(从协变接口添加继承),您仍然可以使其工作
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-19
    • 2014-07-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多