为什么我不能将对象强制转换为它正在实现的接口?

时间:2013-12-17 15:33:31

标签: c# generics collections interface variance

我有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<OneItem>更改为ASet<T> : IASet<T>

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

由于

2 个答案:

答案 0 :(得分:1)

我认为你应该看看我对这个问题的回答,它解释了你的案例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);

如果您需要更多详细信息,则需要更多地解释您的要求。

答案 1 :(得分:0)

这里的问题是您返回ASet的实例,但返回类型为IASet<IItem>ASet类型实际上实现了接口IASet<OneItem>。即使IASet<IItem>实现OneItem,此接口也无法转换为IITem

IASet上的类型参数标有out(即IASet<out T>)时,允许此类型的转化。这会将类型参数标识为协变,并允许此类转换。

不幸的是,这在您的方案中是不可能的。考虑out T的最简单方法是T仅出现在API的输出位置。在这种情况下,T中使用了HashSet<T>。因此,T出现在输入和输出位置,因此T无法标记为out