C#中的差异规则

时间:2013-04-26 22:43:39

标签: c# types covariance contravariance variance

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

如果类型为有效,则为

  

1)指针类型或非泛型类型。

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

  

2)数组类型T [],其中T是共同有效的。

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

  

3)泛型类型参数类型,如果它未被声明为逆变。

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

  

4)构造的类,结构,枚举,接口或委托类型X可能是协同有效的。为了确定它是否,我们以不同方式检查每个类型参数,具体取决于相应的类型参数是声明为covariant(out),contravariant(in)还是invariant(both)。 (当然,类和结构的泛型类型参数永远不会被声明为'out'或'in';它们将始终是不变的。)如果第i个类型参数被声明为covariant,那么Ti必须是covariantly有效的。如果它被宣布为逆变,那么Ti必须是有效的。如果它被声明为不变量,则Ti必须是不变的有效。

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

我们是否在讨论泛型类型的所有in / out / invariant类型参数的方差?根据定义,泛型类型一次可以是一种类型参数的协变/逆变/不变量。为了协变或不变,在这种情况下,立即对它的所有类型参数都没有任何意义。这意味着什么?

前进。为了确定泛型类型是否具有协变有效性,我们检查其类型参数(不是类型参数)。因此,如果相应的类型参数是covariant / contravariant / invariant,那么type参数分别是covariantly / contravariantly / invariantly有效...

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


编辑:谢谢Eric。非常感谢!

我完全理解有效/无差异/不变的有效意义。如果类型绝对不是逆变的,那么类型是有效的,这意味着它可以是不变的。完全没问题!

对于第4条规则,您将遵循如何确定构造的泛型类型是否有效协变的过程,如规则中所定义。但是,如何确定声明为covariant(out)的类型参数是否具有协变有效性?

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

另外,第二条规则:

  

2)数组类型T [],其中T是共同有效的。

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

如果T是引用类型,那么投影T[]T只是变体。

3 个答案:

答案 0 :(得分:13)

你是对的,最后一条规则是最难理解的,但我向你保证它并不含糊。

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

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

此类型是否具有协同有效性

I<string, object, int> { }

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

  

为了确定它是否,我们以不同方式检查每个类型参数,具体取决于相应的类型参数是声明为covariant(out),contravariant(in)还是invariant(both)。

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

  

如果第i个类型参数被声明为协变(out),那么Ti必须是协变有效的。

第二个类型参数是out U,因此object必须是协同有效的。它是。

  

如果它被声明为逆变(in),那么Ti必须是有效的。

第一个被宣布为in T,因此string必须是违反有效的。它是。

  

如果它被声明为不变量,那么Ti必须是不变的。

第三个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的类型参数

  

如果第i个类型参数(W)被声明为协变(out),那么Ti(T)必须是协变有效的。

好的,因此IEnumerable<T> IT的共同有效,T必须是协同有效的。是吗?没有。 in T被声明为in。声明为IEnumerable<T>的类型参数永远不会协同有效。因此,I中使用的{{1}}类型无法协同有效,因为违反了“必须”条件。

同样,就像我在回答你之前的问题时说的那样,如果“有效的共生”和“有效的逆转”会给你带来悲伤,那就给他们不同的名字吧。它们是明确定义的正式属性;如果它让你更容易理解,你可以给他们任何你想要的东西。

答案 1 :(得分:4)

不,你的注释搞砸了。

该文章真的难以理解,部分原因是“有效的共生”与协方差无关。埃里克确实指出了这一点,但它意味着每一句话都必须“重新思考”自然意义,然后根据这些奇怪的定义来思考“有效的共生”,“有效的逆转”和“有效的不变”。

我强烈建议您阅读有关Liskov替换原则的内容,然后考虑可替代性。从LSP的角度来看,协方差,逆变和不变性的定义非常简单。

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

答案 2 :(得分:4)

  

如何确定声明为covariant(out)的类型参数是否具有协变有效性?

阅读规则3。

  

在通用接口I{string, object int>封闭构造接口I<in T, out U, V> 中,不应该将类型参数object声明为协变的事实泛型接口声明中的类型参数out U是否表示类型参数object是协变的?

首先,你正在使用“covariant”,你的意思是“共同有效”。请记住,这些是不同的事情。

其次,让我们再次讨论它。 object是否有共同的效力?是的,按照规则1. I<string, object, int>是否有共同的效力?是的,根据规则3,其中规定:

  • 与T对应的类型参数必须是违反有效的。
  • 与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不是。