在C#中的方差规则(Variance rules in C#)

2019-09-01 05:54发布

该方差的有效性确切规则有点含糊,不具体。 我将列出什么使一个有效的类型,协变的规则,并附加一些查询和个人注解每个这些规则。

A型是有效的协变 ,如果它是:

1)一个指针类型,或一个非通用的类型。

指针和非泛型类型在C#变种,除阵列和非泛型委托。 通用类,结构和枚举是不变的。 我说得对吗?

2)一种阵列型T []其中T是有效的协变。

因此,这意味着,如果元素类型T阵列T[]是协变(参考或数组元素类型),则该数组是协变,并且如果元素类型是不变的(值类型),则数组的类型是不变的。 数组不能在C#中被逆变。 我说得对吗?

3)一种一般类型参数类型,如果它没有声明为逆变。

我们常说,一个泛型类型是变种的参数类型,但对于一个参数类型是它自己的变体。 这是说,另一种缩写形式? 例如,通用型T<out D>是协变D (因此协变有效),因此我们可以说,类型参数D是协变有效。 我对吗?

4)构建类,结构枚举,接口或委托类型X可能是有效的协变。 要确定它是否是,我们检查每个类型参数不同,这取决于相应的类型参数是否被宣布为协变(出),逆变(中),或不变(没有)。 (当然的类和结构的泛型参数永远不会被“在”声明“出来”否则,他们将永远是不变的。)如果第i个类型参数被宣布为协变,那么钛必须是有效的协变。 如果它被宣布为逆变,然后钛必须是有效的contravariantly。 如果它被宣布为不变量,然后钛必须有效目不暇接。

这最后一条规则,从上到下,是完全不明确。

我们是在谈论一个通用型的所有的输入/输出/不变的类型参数的变化? 根据定义,一个通用的类型可以是协变/逆变/上一次一个类型放慢参数是不变的。 要协变或不变,在这种情况下,它的所有的类型参数在一次不抱任何意义。 那会是什么意思呢?

向前进。 要确定是否泛型类型是协变有效,我们审视它的类型参数(不键入paramters)。 因此,如果对应的类型参数是协变/逆变/不变,那么类型参数协变/ contravariantly /分别目不暇接,有效...

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


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

我不完全理解什么有效的协变/ contravariantly /目不暇接的意思。 A型有效covriantly,如果它绝对不是逆变,这意味着它可以是不变的。 完全没有!

对于第4个规则,你跟着如何确定一个构造的泛型类型是否是有效的协变,如在规则中定义的过程。 但是,你如何确定是否属于声明为协变(出)类型参数是协变有效?

例如,在封闭构造接口I {} 通用接口I {...}的, 应该不是张女士的类型参数object被声明为协变类型参数(出U)在通用接口声明的意思该类型参数对象协变? 我认为它应该。 的Cuz这就是被协变的非常清晰。

此外,第二条规则:

2)一种阵列型T []其中T是有效的协变。

什么是数组元素类型T是有效的协变意味着什么呢? 你的意思是元素类型是值类型 (不变在这种情况下)或(在这种情况下,协变)的引用类型?

曲子投影TT[]是唯一变体,如果T是引用类型。

Answer 1:

你是正确的,最后一条规则是最难理解,但我向你保证,这一点也不含糊。

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

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

这类型的协变有效吗?

I<string, object, int> { }

让我们通过我们的定义。

要确定它是否是,我们检查每个类型参数不同,这取决于相应的类型参数是否被宣布为协变(出),逆变(中),或不变(没有)。

OK,所以类型参数是stringobjectint 。 相应的参数是in Tout UV分别。

如果第i个类型参数被宣布为协变( out ),然后钛必须是有效的协变。

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

如果它被宣布为逆变( in ),然后钛必须是有效的contravariantly。

第一个被宣布in T ,这样string必须是有效的contravariantly。 它是。

如果它被宣布为不变量,然后钛必须有效目不暇接。

第三V是不变的,所以int必须有效目不暇接; 它必须是有效的contravariantly和协变两者。 它是。

我们通过所有三项检查; 类型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 。 需要注意的是T是一个类型参数 I和类型参数 IEnumerable

如果第i个类型参数( W )被宣布为协变( out ),然后钛( T )必须是有效的协变。

好了,为IEnumerable<T>I是有效的协变, T必须是有效的协变。 是吗? 没有。 T被宣布为in T 。 被声明的类型参数in从来都不是有效的协变。 因此,类型IEnumerable<T>作为内部使用的I是无效的协变,这是因为“必须”条件被破坏。

同样,像我在回答你刚才的问题说,如果“有效的协变”和“有效contravariantly”是给你的悲伤,只是给他们不同的名字。 他们明确的正规性; 你可以给他们打电话,你想,如果它使你更容易去了解任何东西。



Answer 2:

不,你的注释是搞砸了。

这篇文章是真的很难理解,部分原因是“有效的协变”有什么都没有做协方差。 埃里克不指出这一点,但它意味着,每一句话你要“unthink”自然的含义,然后想在“协变有效”这些奇怪的定义而言,“有效contravariantly”和“有效目不暇接”。

我强烈建议你,而不是阅读的里氏替换原则,再想想可替代性。 当从LSP角度望去协方差,逆变和不变性有非常简单的定义。

然后,您可能会注意到在编译时的C#规则并不完全与LSP匹配(遗憾的是 - 这主要是在Java中取得并复制到C#来帮助法院Java程序员错误)。 在另一方面,在运行时LSP规则必须遵守,所以如果你开始与这些,你会写代码,无论编译和运行正确,我认为这是一个更有价值的努力比学习C#语言的规则(除你正在写一个C#编译器)。



Answer 3:

你怎么确定是否属于声明为协变(出)类型参数是协变有效?

阅读规则3。

闭合构造接口I{string, object int>通用接口的I<in T, out U, V>应该不是非常事实,即类型参数object被声明为协变型参数out U在通用接口声明意味着该类型参数object是协变的?

首先,你使用的“协变”,你的意思是“协变合法”。 请记住,这是不同的东西。

其次,让我们再经历一遍。 为object协变有效吗? 是的,规则1是I<string, object, int>协变有效吗? 是的,规则3,其中指出:

  • 对应于T中的类型参数必须是contravariantly有效。
  • 与U对应的类型参数必须是协变有效。
  • 对应到V的类型参数必须是这两者。

由于所有这三个条件都满足, I<string, object, int>是协变有效。

在“数组类型T [],其中T是有效的协变”是什么的数组元素类型T是有效的协变意思?

我不明白的问题。 我们定义什么是“协变有效”的意思。 第2条是“协变有效”定义的一部分。

作为一个例子,是object[]协变有效? 是的,因为object是协变有效。 如果我们有:

interface IFoo<out T> { T[] M(); }

T[]协变有效吗? 是的,因为T是协变有效。

如果我们有

interface IBar<in T> { T[] M(); }

T[]协变有效吗? 号对于数组类型为协变有效元素类型必须是协变有效,但T不是。



文章来源: Variance rules in C#