c#中的方差规则



方差有效性的确切规则有点模糊,不具体。我将列出使类型有效的规则-协变,并为每个规则附加一些查询和个人注释。

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

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

指针和非泛型类型在c#中没有变化,除了数组和非泛型委托。泛型类、结构和枚举是不变的。我说的对吗?

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

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

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

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

一个构造的类、结构、枚举、接口或委托类型X可能协变有效。为了确定它是否存在,我们以不同的方式检查每个类型实参,这取决于相应的类型形参是被声明为协变(out)、逆变(in)还是不变(两者都不是)。(当然,类和结构的泛型类型参数永远不会被声明为'out'或'in';它们永远是不变的。)如果第i个类型参数被声明为协变,那么Ti必须协变有效。如果它被声明为逆变,那么Ti一定是逆变有效的。如果它被声明为不变的,那么Ti一定是不变有效的。

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

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

前进。为了确定泛型类型是否协变有效,我们检查它的类型实参(不是类型形参)。因此,如果对应的类型参数为协变/逆变/不变,则类型参数分别为协变/逆变/不变有效…

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


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

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

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

例如,在封闭构造接口I {}泛型接口I{…}类型参数object在泛型接口声明中被声明为协变类型参数(out U)这一事实难道不应该意味着类型参数对象是协变的吗?我想应该是这样。因为这就是协变的定义。

同样,第二条规则:

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

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

因为投影T →只有当T是引用类型时,T[]才是变量。

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

举一两个例子会有帮助。考虑以下类型声明:

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

这个类型协变有效吗?

I<string, object, int> { }

让我们看一下我们的定义。

要确定是否存在,根据相应的类型形参是被声明为协变(out)、逆变(in)还是不变(两者都不是),对每个类型实参进行不同的检查。

好的,所以类型参数是string, objectint。对应的参数分别为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();
}

I中的IEnumerable<T>是一个类型。IEnumerable<T>内使用I有效协变?

让我们看一下定义。类型实参T对应于类型形参out WTI的类型参数IEnumerable的类型参数

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

OK,所以I中的IEnumerable<T>协变有效,T必须协变有效。是吗?不。将T声明为in T。声明为in的类型参数在协变上永远无效。因此,I内部使用的IEnumerable<T>类型在协变上是无效的,因为违反了"must"条件。

再一次,就像我在回答你之前的问题时说的,如果"有效协变"one_answers"有效逆变"让你感到悲伤,那就给它们起个不同的名字。它们是定义良好的形式属性;你可以叫它们任何你想叫的名字,如果这样更容易理解的话。

不,你的注释乱成一团

那篇文章真的很难理解,部分原因是"有效协方差"与协方差完全没有关系。Eric确实指出了这一点,但这意味着对于每个句子,你都必须"取消思考"自然意义,然后根据"协变有效"、"逆变有效"one_answers"不变有效"这些奇怪的定义来思考。

我强烈建议你阅读一下Liskov替代原则,然后再考虑可替代性。从LSP的角度来看,协方差、逆变性和不变性的定义非常简单。

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

如何确定一个类型参数被声明为协变(out)是否协变有效?

读取规则3

泛型接口I<in T, out U, V>封闭构造接口I{string, object int> 中的

,类型参数object在泛型接口声明中被声明为协变类型参数out U,这一事实难道不应该意味着类型参数object是协变的吗?

首先,你用"协变"来表示"协变有效"。记住,这是不同的东西。

第二,让我们再看一遍。object协变有效吗?是的,根据第一条规则。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不是。

相关内容

  • 没有找到相关文章

最新更新