【问题标题】:Understanding Illegal Generic Cast了解非法通用演员表
【发布时间】:2015-12-23 12:52:30
【问题描述】:

我无法理解为什么我正在执行的演员表会引发运行时异常,说明这是非法演员表。我做了一些研究,发现了this answer,这使我找到了 MSDN 文章Covariance and Contravariance in Generics。但是,我仍然有点困惑,所以如果有人能帮助澄清,将不胜感激。

这是 2 类类型的对象层次结构:

IMongoEntity (interface)
  |   - MongoEntity (abstract)
  |     |    -SalesProject (concrete)
  |     |    -ManagementProject (concrete)

IEntityService<T> where T : IMongoEntity (interface)
  |      -EntityService<T> where T : IMongoEntity (concrete superclass)
  |       |    - MgmtService : EntityService<ManagementProject> (subclass)
  |       |    - SalesService : EntityService<SalesProject> (subclass)

仅创建了两个非通用服务,因此我可以创建一些仅适用于这些特定类型的特定方法(本质上是预定义的数据库查找)。

然后我有这条线,它抛出InvalidCastException

IEntityService<IMongoEntity> service = fromsales ? 
    (IEntityService<IMongoEntity>)salesService : 
    (IEntityService<IMongoEntity>)mgmtService;

既然两个服务都是从同一个接口和抽象类派生的,并且使用的类型参数是从同一个抽象类派生的,那么为什么这种强制转换是非法的呢?

注意:我有解决方法,所以我并不是真的在寻找解决方案,而是想了解为什么这是不允许的。

【问题讨论】:

  • 如果 IEntityService&lt;T&gt; where T : IMongoEntity 更改为 IEntityService&lt;out T&gt; where T : IMongoEntity 我认为它会起作用。您正在遇到协方差问题
  • @ScottChamberlain 不是泛型中返回类型的 out 关键字吗?这些方法都返回 Task 或 Task
  • 想象IEntityService&lt;T&gt; 有一个Add(T item) 方法。 (我们不知道它是否这样做,但编译器并不关心。它可以。)现在想想这对IEntityService&lt;IMongoEntity&gt; 意味着什么 - 你可以添加 any mongo 实体。现在,您可以将ManagementProject 添加到SalesService 吗?不... 这就是它无效的原因。
  • @JonSkeet 啊,就是这么简单!这是有道理的。
  • out 告诉编译器您没有任何Add(T item) 调用。

标签: c# .net generics casting


【解决方案1】:

MyType&lt;Base&gt;MyType&lt;Derived&gt; 没有任何继承关系,即使Derived 派生自Base。这两种泛型类型只是两种不同的类型。

解决此问题的一种方法是使用非通用接口作为基本接口:

public interface IEntityService
{
    void DoSomething(object item);
}

public interface IEntityService<T> : IEntityService
{
    void DoSomething(T item);
}

此模式用于 .Net 类库(例如 IEnumerable/IEnumerable&lt;T&gt;IList/IList&lt;T&gt;)。

如果您知道您的接口仅将泛型类型 (T) 用于输出,则可以使用 out 关键字 IMyInterface&lt;out T&gt;。然后,您可以为T 提供更多派生类型。这称为协方差。然后方法的返回值将产生消费者期望的更多派生类型,这没关系。

如果它仅将泛型类型用于输入,请使用 in 关键字 IMyInterface&lt;in T&gt;。然后,您可以为T 提供一个派生程度较低的类型。这称为逆变。然后方法的输入参数将按预期获得更多派生类型,这没关系。

【讨论】:

    猜你喜欢
    • 2019-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多