h5响应式网站模板,网站开发相关外文书籍,平台型网站制作,网站建站商务平台环境#xff1a;window 10.netcore 3.1vs2019 16.5.1一、为什么要有协变#xff1f;首先看下面的代码#xff1a;还有下面的#xff1a;其实上面报错的是同一个问题#xff0c;就是你无法用ListFruit指向ListApple#xff01;我们的疑问在于#xff0c;… 环境window 10.netcore 3.1vs2019 16.5.1一、为什么要有协变首先看下面的代码还有下面的其实上面报错的是同一个问题就是你无法用ListFruit指向ListApple我们的疑问在于明明是一个盛放苹果的箱子我们说它可以盛放水果怎么了下面我来说一下原因首先不能根据这个类的用途去判断因为你无法保证List这个类一定是集合List当然是集合但如果是PersonT呢它是做什么的只是盛放东西吗。其次Apple继承自Fruit没错但ListApple和ListFruit压根就没有继承的说法它们是不同的类型泛型参数类型不同也是不同的类型Console.WriteLine(typeof(ListApple) typeof(ListFruit));输出为false所以我们用ListFruit去表示ListApple引发报错很正常但是从我们程序员角度来说这样肯定不方便那么有没有解决办法呢答案有它就是协变二、什么是协变首先明确一下目的我们想让ListFruit list new ListApple();这类代码成立这行代码肯定不成立我说的是这类代码想要达到我们的目的肯定是要有规则的必须使用接口进行指向不能使用类比如说我们只能这么写IListFruit list new ListApple();虽然这样写也报错不能够这么写ListFruit list new ListApple();为什么不能使用类因为类里面牵扯到的内容比较多而下一条规则就说了方法的入参不能使用泛型参数所以为了尽量把这种约束的范围变小一点我们也应该在接口上加规则约束而不是直接在类上这一点是我猜的。这个接口的泛型参数只能用来做接口内方法的返回值不能用作接口内方法的参数在泛型参数前加out关键字实现这里从两方面说1.允许这个泛型参数做返回值比如定义接口ITestout T允许T作为接口内方法MethodA的返回值T MethodA();。在使用的时候你用ITestFruit指向ITestApple那么当调用ITestFruit的方法MethodA的时候你得到的返回类型声明是Fruit实际上你得到的返回类型是Apple所以一点问题没有。2.禁止这个泛型参数做方法的入参比如定义接口ITestout T允许T作为接口内方法MethodA的入参void MethodA(T t);。在使用的时候你用ITestFruit指向ITestApple那么当调用ITestFruit的方法MethodA的时候你看到这个方法要求传入一个Fruit所以你可能传一个orange橙子也继承了Fruit进去但人家实际上是ITestApple要求传入的是Apple这样肯定说不通所以泛型参数禁止做方法的入参上面说了规则那么下面来一个实例可以看到我们按照规则在ITest的泛型参数T上加了out后整个程序腰不酸了、腿不疼了。事实上微软在集合的定义上已经考虑到了这一点看一下IEnumerable的定义所以我们像下面这样写也没有错讲到这里我们可以说一下什么是协变了假如有两个类A和AA其中AA继承自A如果此时有一个泛型接口ICout T那么可以认为ICA能指向ICAA即ICAA和ICA的关系看着像AA和A的关系一样只是看着像并且能单方向转换但不是继承。三、什么是逆变逆变和协变是相对的具体来说逆变的目的是让ListApple test new ListFruit();这类代码成立这行代码肯定报错我说的是这类代码你一定认为这疯了“说一个盛放水果的箱子盛放的是苹果”肯定不对。但是我们看下面的实例上图中的代码是不是颠覆了你的认知好吧这就是逆变一个可以让你用ITestApple去指向TestFruit()的存在这里还是再说一下逆变的规则必须使用接口进行指向不能使用类这一点和协变是一样的。这个接口的泛型参数只能用来做接口内方法的入参不能用作接口内方法的返回值在泛型参数前加in关键字实现这里从两方面说1.允许这个泛型参数做方法的入参比如定义接口ITestin T允许T作为接口内方法MethodA的入参void SetValue(T t);。在使用的时候你用ITestApple指向ITestFruit那么当你调用ITestApple的方法MethodA的时候你看到这个方法要求传入一个Apple实际上人家是ITestFruit人家要求传入的是Fruit所以这里一点问题没有。2.禁止这个泛型参数做方法的返回值比如定义接口ITestin T允许T作为接口内方法MethodA的返回值T GetValue();。在使用的时候你用ITestApple指向ITestFruit那么当调用ITestApple的方法MethodA的时候你得到的返回类型声明是Apple但实际上人家是ITestFruit所以返回的是一个orange橙子也继承了Fruit也说不定所以你用Apple去接收这个返回值肯定不行的所以泛型参数禁止做方法的返回值四、委托内的协变和逆变委托中的泛型参数是天然就可以支持协变或逆变中的一种的对这句话的理解如下如果你这么定义委托public delegate T GetValueT();那么它天然支持协变因为T只用来声明返回值如下代码如果你这么定义委托public delegate void SetValueT(T t);那么它天然支持逆变因为T只用来做入参如下代码如果你这么定义委托它既不支持协变也不支持逆变public delegate T DealT(T t);因为T即用来做入参也用来做返回值如下代码其实在委托中为了更好的表示泛型参数是支持协变还是逆变最好是定义的时候就用out或in参数进行声明比如public delegate T GetValueout T();//支持协变public delegate void SetValuein T(T t);//支持逆变微软在Func、Action系列委托中已经为我们做了示范