c# - C# 中的变体规则

标签 c# types covariance contravariance variance

Exact rules for variance validity有点模糊,不具体。我将列出使类型有效协变的规则,并为每条规则附加一些查询和个人注释。

如果满足以下条件,则类型协变有效:

1) a pointer type, or a non-generic type.

指针和非泛型类型在 C# 中不是变体,数组和非泛型委托(delegate)除外。通用类、结构和枚举是不变的。我在这里吗?

2) An array type T[] where T is valid covariantly.

所以这意味着如果元素类型为T数组的 T[]是协变的(引用或数组元素类型),则数组是协变的,如果元素类型是不变的(值类型),则Array类型是不变的。数组在 C# 中不能是逆变的。我在这里吗?

3) A generic type parameter type, if it was not declared as being contravariant.

我们通常说泛型是参数类型的变体,但参数类型本身就是变体。这是另一种简写形式吗?例如,泛型类型 T<out D>D 上是协变的(因此协变有效),因此我们可以说类型参数 D是协变有效的。我对吗?

4) A constructed class, struct, enum, interface or delegate type X might be valid covariantly. To determine if it is, we examine each type argument differently, depending on whether the corresponding type parameter was declared as covariant (out), contravariant (in), or invariant (neither). (Of course the generic type parameters of classes and structs will never be declared 'out' or 'in'; they will always be invariant.) If the ith type parameter was declared as covariant, then Ti must be valid covariantly. If it was declared as contravariant, then Ti must be valid contravariantly. If it was declared as invariant, then Ti must be valid invariantly.

最后一条规则,从上到下,是完全模棱两可的。

我们是在谈论泛型类型在其所有输入/输出/不变类型参数上的变化吗?根据定义,泛型类型一次可以在一个类型参数上是协变/逆变/不变的。协变或不变,在这种情况下,一次对所有类型参数都没有任何意义。那是什么意思?

前进。为了确定泛型类型是否协变有效,我们检查它的类型参数(不是类型参数)。所以如果对应的类型参数是协变/逆变/不变的,那么类型参数分别是协变/逆变/不变有效...

我需要更深入地解释这条规则。


编辑:谢谢埃里克。非常感谢!

我完全理解 valid covariantly/contravariantly/invariantly 的意思。如果一个类型绝对不是逆变的,那么它就是协变有效的,这意味着它可以是不变的。非常好!

对于第 4 条规则,您将按照规则中定义的如何确定构造的泛型类型是否协变有效的过程进行操作。但是,如何确定声明为协变 (out) 的类型参数是否协变有效?

例如,在通用接口(interface) I { ... } 封闭构造接口(interface) I { } 中,不应该是事实 即类型参数 object在泛型接口(interface)声明中被声明为协变类型参数(out U) 意味着类型参数对象是协变的?我认为应该。因为这就是协变的定义。

另外,第二条规则:

2) An array type T[] where T is valid covariantly.

什么数组元素类型T being valid covariantly 是什么意思?您是说元素类型是值类型(在这种情况下是不变的)还是引用类型(在这种情况下是协变的)?

因为投影 TT[]只有在 T 时才是变体是引用类型。

最佳答案

你是对的,最后一条规则是最难理解的,但我向你保证它没有歧义。

一两个例子会有所帮助。考虑这种类型声明:

interface I<in T, out U, V> { ... }

这种类型协变有效吗?

I<string, object, int> { }

让我们来看看我们的定义。

To determine if it is, we examine each type argument differently, depending on whether the corresponding type parameter was declared as covariant (out), contravariant (in), or invariant (neither).

好的,所以类型参数是string , objectint .对应参数为in T , out UV , 分别。

If the ith type parameter was declared as covariant (out), then Ti must be valid covariantly.

第二个类型参数是out U , 所以 object必须协变有效。是的。

If it was declared as contravariant (in), then Ti must be valid contravariantly.

第一个被声明为in T , 所以 string必须是逆变有效的。是的。

If it was declared as invariant, then Ti must be valid invariantly.

第三个V是不变的,所以 int必须始终有效;它必须在逆变和协变上都有效。是的。

我们通过了所有三项检查;类型 I<string, object, int>是协变有效的。

好吧,那个很简单。

现在让我们来看一个更难的问题。

interface IEnumerable<out W> { ... }
interface I<in T, out U, V> 
{
    IEnumerable<T> M();
}

IEnumerable<T>里面I是一种类型。是IEnumerable<T>内部使用I有效协变?

让我们来看看我们的定义。我们有类型参数 T对应类型参数 out W .注意 TI 的类型参数IEnumerable 的类型参数 .

If the ith type parameter (W) was declared as covariant (out), then Ti (T) must be valid covariantly.

好的,所以 IEnumerable<T>I协变有效,T必须协变有效。是吗?不。 T被声明为 in T .声明的类型参数 in永远不是协变有效的。因此类型 IEnumerable<T>内部使用 I协变无效,因为违反了“必须”条件。

同样,就像我在回答您之前的问题时所说的那样,如果“协变有效”和“逆变有效”让您感到悲伤,只需给它们不同的名称。它们是定义明确的正式属性;如果能让您更容易理解,您可以随心所欲地调用它们。

关于c# - C# 中的变体规则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16246281/

相关文章:

c++ - 为什么多态类型错误和清理问题?

python - Python Numpy计算的协方差矩阵每次都变化

matlab - 在 Matlab 中估计样本协方差矩阵特征值的方差

c++ 自动覆盖有符号/无符号

haskell - 有没有办法让 GHC 提供类型孔的类型类约束?

java - 如何在ArrayList中使用多态性?

c# - Visual Studio 调试器未附加

c# - 多种类型的数组 C#(包括其他数组)

c# - 不引发异常

c# - 获取谷歌广告 ID 并限制广告