【问题标题】:Why I cannot cast an object to an interface it is implementing?为什么我不能将对象转换为它正在实现的接口?
【发布时间】:2014-01-05 10:55:20
【问题描述】:

我有 2 个供客户使用的公共接口

public interface IASet<T> where T : IItem {
    HashSet<T> Collection { get; set; }
}

public interface IItem {
    string Name { get; set; }
}

这个想法是让客户端通过这个对象来访问数据:

IASet<IItem> aset = GetIt();
foreach(IItem i in aset.Collection)
    Console.WriteLine(i.Name);

具体实现如下:

private class ASet : IASet<OneItem>{
    public HashSet<OneItem> Collection { get; set; }
}
private class OneItem : IItem{
    public string Name { get; set; }
}

最后是生成对象的函数:

public static IASet<IItem> GetIt()
{
    ASet a = new ASet();
    a.Collection = new HashSet<OneItem>();
    a.Collection.Add(new OneItem() { Name = "one" });
    a.Collection.Add(new OneItem() { Name = "two" });
    //for test :
    foreach (IItem i in a.Collection)
    {
        Console.WriteLine(i.Name);
    }

    /*Error 1   Cannot implicitly convert type 'ConsoleApplication2.Program.ASet'
     * to 'ConsoleApplication2.IASet<ConsoleApplication2.IItem>'. An explicit
     * conversion exists (are you missing a cast?)
    */
    //return a;


    /*Unable to cast object of type 'ASet' to type
     * 'ConsoleApplication2.IASet`1[ConsoleApplication2.IItem]'.
     */
    return (IASet<IItem>)a;
}

因为我的真实代码没有这个简单,我不能把ASet : IASet&lt;OneItem&gt;改成ASet&lt;T&gt; : IASet&lt;T&gt;这样的东西

能否请您解释一下为什么我不能这样做,以及一些解决问题的建议?

谢谢

【问题讨论】:

  • 因为 IASet 上的T 是不变的。它是这样声明的,IASet&lt;out T&gt;,它会起作用 - 但你不能这样做,因为 HashSet 不会接受它。
  • 我相信这只有在IASet 的泛型类型被定义为out 模板参数时才有效。请参阅msdn.microsoft.com/en-us/library/dd469487.aspx 没有什么可以阻止某人将不同类别的IItem 对象添加到集合中的集合中。

标签: c# generics collections interface variance


【解决方案1】:

这里的问题是您正在返回一个ASet 的实例,但返回类型是IASet&lt;IItem&gt;ASet 类型实际上实现了接口IASet&lt;OneItem&gt;。即使OneItem 实现IITem,此接口也不能转换为IASet&lt;IItem&gt;

IASet 上的类型参数标有out(即IASet&lt;out T&gt;)时,允许这种类型的转换。这会将类型参数标识为协变并允许这种类型的转换。

不幸的是,这在您的场景中是不可能的。想到out T 的最简单方法是T 只出现在API 的输出位置。在这种情况下,T 用于HashSet&lt;T&gt;。在这种用法中T 出现在输入和输出位置,因此T 不能标记为out

【讨论】:

    【解决方案2】:

    我想你应该看看我对这个问题的回答,它解释了你的情况Interface with generic object of the interface type

    顺便说一句,我建议你对你的 GetIt 函数做这样的修改:

    public static IASet<T> GetIt<T>() 
      where T : IItem
    {
      ASet a = new ASet();
      a.Collection = new HashSet<OneItem>();
      a.Collection.Add(new OneItem() { Name = "one" });
      a.Collection.Add(new OneItem() { Name = "two" });
      //for test :
      foreach (var i in a.Collection)
      {
        Console.WriteLine(i.Name);
      }
    
      /*Error 1   Cannot implicitly convert type 'ConsoleApplication2.Program.ASet'
       * to 'ConsoleApplication2.IASet<ConsoleApplication2.IItem>'. An explicit
       * conversion exists (are you missing a cast?)
      */
      //return a;
    
    
      /*Unable to cast object of type 'ASet' to type
       * 'ConsoleApplication2.IASet`1[ConsoleApplication2.IItem]'.
       */
      return (IASet<T>)a;
    }
    

    然后你需要这样称呼它:

      IASet<OneItem> aset = GetIt<OneItem>();
      foreach (IItem i in aset.Collection)
        Console.WriteLine(i.Name);
    

    如果您想了解更多细节,您必须更多地解释您的要求。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-05-08
      • 1970-01-01
      • 2010-12-08
      • 1970-01-01
      • 2019-07-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多