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
只是变体。
答案 0 :(得分:13)
你是对的,最后一条规则是最难理解的,但我向你保证它并不含糊。
一两个例子会有所帮助。考虑这种类型声明:
interface I<in T, out U, V> { ... }
此类型是否具有协同有效性?
I<string, object, int> { }
让我们来看看我们的定义。
为了确定它是否,我们以不同方式检查每个类型参数,具体取决于相应的类型参数是声明为covariant(out),contravariant(in)还是invariant(both)。
好的,所以类型参数是string
,object
和int
。相应的参数分别为in T
,out U
和V
。
如果第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
。请注意,T
是I
的类型参数和IEnumerable
的类型参数。
如果第i个类型参数(
W
)被声明为协变(out
),那么Ti(T
)必须是协变有效的。
好的,因此IEnumerable<T>
I
中T
的共同有效,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,其中规定:
由于满足所有三个条件,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
不是。