【问题标题】:Supporting both covariance and contravariance for a single type parameter [duplicate]支持单个类型参数的协变和逆变[重复]
【发布时间】:2014-10-09 20:07:49
【问题描述】:

可能重复:
Covariance and Contravariance on the same type argument

您可以使用 out 关键字将泛型类型参数声明为协变:

interface ICovariant<out R>

您可以使用in 关键字将泛型类型参数声明为逆变:

interface IContravariant<in R>

你也可以同时支持不同类型的参数:

interface IVariant<out R, in A>

那么为什么你不能同时支持一个类型参数呢?

【问题讨论】:

标签: c# generics interface covariance contravariance


【解决方案1】:

那么为什么你不能同时支持一个类型参数呢?

请记住,如果类型参数是输出安全的,则接口只能在类型参数中是协变的,如果类型参数是输入安全的,接口只能在类型参数中是逆变的。

语法out T 表示T 是一个协变类型参数。

语法in T 表示T 是逆变类型参数。

由于T 是一个协变类型参数,因此根据定义它是输入不安全的。

由于T 是逆变类型参数,因此它定义为输出不安全。

因此,T 是输入不安全和输出不安全的。

因此,T 在输入位置被禁止,T 在输出位置被禁止。

因此,T 不能出现在输入位置,也不能出现在接口指定的任何方法的任何输出位置。

因此,T根本不能在接口上使用,作为类型参数毫无意义。因此,语言设计者甚至禁止您在接口上包含标记为协变和逆变的这种无用类型,以避免丑陋

interface IFoo<in and out T> { }
Foo<T> : IFoo<T> { }

然后:

IFoo<Cat> cat = (IFoo<Animal>)new Foo<Dog>();

(如果您需要阅读输入安全和输出安全,请参阅语言规范的 13.1.3.1。)

【讨论】:

  • 哇,这是一个很难阅读的答案:)
  • @JonH:谢谢,我又试了一次。让我知道你的想法?
  • 就可以了。虽然我的大脑现在开始有点疼了。
  • 实际上,在某些情况下,接口包含实际上不参与任何方法或属性的类型参数可能很有用。更大的问题是,如果对于从 Object 派生的任何 T 和 U,Foo 是 Foo 的有效替代品,而 Foo 是 Foo 的有效替代品,那么 Foo 是Foo 的有效替代品。由于协变和逆变只适用于 Object 的导数,因此说接口在 T 中是协变和逆变的将使 T 毫无意义。
【解决方案2】:

这行不通。考虑一下(如果存在in out):

public class INewList<in out T>
{
    public T DoIt(T item);
}

这是不可能满足的,因为期望INewList&lt;T&gt; 的人将与更窄和更宽类型的接口兼容。

考虑INewList&lt;Feline&gt;

如果 in/out 都可以,则此接口将等效于 INewList&lt;Animal&gt;,但这对于 in 位置无效,因为它允许您扩大类型参数:

... DoIt(Animal item)

这不起作用,因为这意味着您可以传入一个 new Dog() 实例,在该实例中,原始界面应该是猫。

在出局位置上也是相反的,因为它允许:

Puma DoIt(...)

这将是无效的,因为原始界面可以传回任何猫科动物,不一定是 Puma。

【讨论】:

  • 抱歉,在我看到@Jason 已经回答之前,我正在研究我的答案。
  • 不要道歉 :-) 对同一件事有不同的方式总是很高兴。
  • @James Michael Hare:哈!我改进了我的答案,在没有看到你的情况下包含了一个琐碎的猫/狗示例!
  • 这太有趣了@Jason!这里似乎在同一个波长上!
猜你喜欢
  • 2018-12-24
  • 1970-01-01
  • 2013-07-11
  • 2013-11-29
  • 1970-01-01
  • 1970-01-01
  • 2012-04-07
  • 1970-01-01
相关资源
最近更新 更多