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是指明确的接口实现,那里的答案会让我觉得我不应该遇到问题。
答案 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 { }