【问题标题】:Why do I have to explicitly cast a class to its interface type? [duplicate]为什么我必须将一个类显式地转换为它的接口类型? [复制]
【发布时间】:2018-07-13 12:15:54
【问题描述】:

TL;DR: 如果 List 实现了 ICollection,而 Facility 实现了 IAmAHierarchyNode,为什么我不能隐式将 List 转换为 ICollection


我收到“无法将类型 '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 是我找到的最接近的。那里的第二个答案说

如果类型 T1 和 T2 相同,则只能将 List 转换为 ICollection

所以我正在从 List 转换为 ICollection。我的 Facility 类实现了 IAmAHierarchyNode,所以我不明白为什么它们不会被视为相同。

可悲的是,this SO question 是 OP 的拼写错误。 This one 指的是显式接口实现,那里的答案会让我认为我不应该有问题。

【问题讨论】:

  • 老实说,过于混杂的隐式转换是不好的。强类型化是你最大的朋友。如果您没有,请查看此处的 PHP 和 Javascript 示例:sandraandwoo.com/2015/12/24/… 曾经在 PHP 中实际编程过一次,我希望我有很强的类型化。
  • 编译器一定认为存在丢失信息的风险,我前段时间也有同样的问题,但阅读了docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/… 并决定不发布,因为无论如何强烈建议使用强类型
  • 这里我们实际上有一个演员表(列表到 ICollection)和一个泛型的“方差”机制在工作(IAmAHierarchyNode 的设施)。而且我猜编译器只是不敢尝试同时进行强制转换和方差更改。
  • "如果类型 T1 和 T2 相同,您只能将 List 转换为 ICollection。" - “所以我从 List&lt;Facility&gt; 转换为 ICollection&lt;IAmAHierarchyNode&gt;” - 它们不一样。虽然Facility 可能是IAmAHierarchyNode,但IAmAHierarchyNode 不一定是Facility。这就是为什么它们不一样并且引用不适用的原因。
  • “我的 Facility 类实现了 IAmAHierarchyNode,所以我不明白为什么它们不会被认为是相同的”——在你引用的帖子中,这个词“相同”用于表示完全相同。不是 OOP 意义上的“is a”,而是字面上的完全相同的类型。现在有例外,对接口使用泛型类型变化,但这些例外不适用于您的示例。 许多 现有的关于 Stack Overflow 讨论泛型类型差异的问题和答案已经完全涵盖了您的问题。

标签: c#


【解决方案1】:

您可以添加演员表,但它在运行时不起作用。

在 .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 interfacesIEnumerable&lt;out T&gt; 是协变的,因此您可以将派生类型的可枚举分配给基类型可枚举的引用

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

允许你想要的演员的问题是ICollection&lt;T&gt; 允许你添加到集合中。在上面的示例中,如果您确实设法将ICollection&lt;B&gt; 强制转换为ICollection&lt;A&gt;,并且您有另一个派生类型class C : A{},您可以将C 传递给ICollection&lt;A&gt; 的引用,它实际上期望Bs

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

【讨论】:

  • 链接重要引用:对于接口,协变类型参数可以作为接口方法的返回类型,逆变类型参数可以作为接口方法的参数类型。
  • 因为这是无效的:Network.Children.Add(new Network()); 因为Network 不是Facility
  • @Wouter 是的,很好的例子
【解决方案2】:

你不能这样做的原因是ICollection(T) 是不变的。您可以使用List&lt;Facility&gt;IEnumerable&lt;IAmAHierarchyNode&gt; 完成您想要的行为,但我假设您希望能够使用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 { }

【讨论】:

    猜你喜欢
    • 2013-04-15
    • 1970-01-01
    • 1970-01-01
    • 2011-07-22
    • 1970-01-01
    • 1970-01-01
    • 2023-03-30
    • 1970-01-01
    • 2011-03-10
    相关资源
    最近更新 更多