为什么我必须显式地将类转换为其接口类型?

时间:2018-02-02 23:02:28

标签: c#

TL; DR: 如果List< T>实现ICollection< T>,Facility实现IAmAHierarchyNode,为什么我隐式转换List< Facility>到ICollection< IAmAHierarchyNode>?

我收到错误“无法将类型'Foo'隐式转换为'Bar'。存在显式转换(您是否错过了演员?)”

以下是代码:

public interface IAmAHierarchyNode<IdType> {
      IdType Id {
         get;set;
      }

      ICollection<IAmAHierarchyNode<IdType>> Children {
         get;
      }

      IAmAHierarchyNode<IdType> Parent {
         get;set;
      }
}

public class Network : IAmAHierarchyNode<Guid> {
   public List<Facility> Facilities
   {
      get;set;
   }

   // This is where it's making me do an explicit conversion. But why?
   public ICollection<IAmAHierarchyNode<Guid>> Children => Facilities;

   public IAmAHierarchyNode<Guid> Parent {
      get => throw new InvalidOperationException ();
      set => throw new InvalidOperationException ();
   }
}

public class Facility : IAmAHierarchyNode<Guid> {
   public Network Network {
      get;set;
   }

   public Guid NetworkId {
      get;set;
   }

   public ICollection<IAmAHierarchyNode<Guid>> Children {
      get => throw new NotImplementedException ();
   }

   public IAmAHierarchyNode<Guid> Parent {
      get => Network;
      set => throw new NotImplementedException ();
   }
}

以下是我在研究中发现的内容:

This SO question是我发现的最接近的。那里的第二个答案是

  

您只能转换List&lt; T1&gt; ICollection&lt; T2&gt;如果类型T1和T2相同。

所以我从List&lt; Facility&gt;转换到ICollection&lt; IAmAHierarchyNode&gt;。我的Facility类实现了IAmAHierarchyNode,所以我不明白为什么它们不会被认为是相同的。

可悲的是,this SO question是OP的一个错字。 This one是指明确的接口实现,那里的答案会让我觉得我不应该遇到问题。

2 个答案:

答案 0 :(得分:4)

您可以添加强制转换,但在运行时无效。

在.NET中,通用类或接口的不同实例化在运行时被视为单独的类型,因此两个通用实例不共享继承关系,即使它们的通用参数共享继承关系:

class A { }
class B: A { }
List<B> b = new List<B>();
ICollection<A> ca = (ICollection<A>)b; // Will cause a runtime error, you can cast at compile time but the cast will fail at runtime.

此规则covariant/contravariant interfaces有一个例外。 IEnumerable<out T>是协变的,因此您可以将派生类型的枚举分配给基类型的可枚举的引用

List<B> b = new List<B>();
IEnumerable<A> ca = b; // This works without any cast

允许您想要的演员表的问题是ICollection<T>允许您添加到集合中。在上面的示例中,如果您设法将ICollection<B>转换为ICollection<A>,并且您有另一个派生类型class C : A{},则可以将C传递给ICollection<A>的引用实际上需要B s

class A { }
class B: A { }
class C: A { }
List<B> b = new List<B>();
ICollection<A> ca = (ICollection<A>)b; 
ca.Add(new C()); // This would be legal if the above cast would succeed,    
                 // and you would have a List<B> containing a C

答案 1 :(得分:1)

你无法做到这一点的原因是ICollection(T)是不变的。您可以使用List<Facility>IEnumerable<IAmAHierarchyNode>完成所需的操作,但我认为您希望能够使用ICollection(T)提供的方法。

当然,您可以在界面定义中看到这种差异:

public interface ICollection<T> : IEnumerable<T>, IEnumerable

public interface IEnumerable<out T> : IEnumerable

out将通用接口标记为协变,从而允许更多派生类型。看一下下面的例子:

void Main()
{
    var x = new List<A>();
    IEnumerable<I> y = x; // Fine
    ICollection<I> z = x; // Compile Error
}

public class A : I { }

public interface I { }
相关问题