前言
在. NET 4 之前, 泛型接口是不变的..NET 4 通过协变和抗变为泛型接口和泛型委托添加了一个重要的扩展. 协变和抗变指对参数和返回值的类型进行转换.
我们来看下到底什么是协变什么是抗变:
如果某个返回的类型可以由其基类替换, 那么这个类型就是支持协变的
如果某个参数类型可以由其派生类替换, 那么这个类型就是支持逆变 (抗变) 的.
函数的类型转换
在理解协变与抗变之前, 我们看下面这个例子:
- class Program
- {
- public static string Tmain(object o)
- {
- return "aaa";
- }
- static void Main(string[] args)
- {
- string a = "aaa";
- object b = Tmain(a);
- }
- }
我们仔细看下这个传值和返回. 注意其中发现了两次隐式转换.
1, 向函数传值的时候 参数 a 从 string 类型转换成 object 类型
2, 最后接收返回值的时候 b 由 string 类型转换成 object 类型
我们在返回函数来看.
1, String Tmain(object o) 可以转换成 string Tmain(string o)
2, String Tmain(string o) 可以转换成 object Tmain(string o)
在这里, 也就是说函数输入的时候输入类型可以从 object 转换成 string. 基类 - 派生类
在函数输出时, 函数的输出类型 (返回类型) 从 string 转换成 object. 派生类 - 基类.
这里就比较接近泛型接口的协变和抗变的概念了. 我们再看我们开头的概念
如果某个返回的类型可以由其基类替换, 那么这个类型就是支持协变的如果某个参数类型可以由其派生类替换, 那么这个类型就是支持逆变 (抗变) 的.
理解泛型接口的协变和抗变(in,out)
我们下面来看看泛型接口的协变及抗变的例子:
首先我们看下协变, 在 C# 高级编程 (第十一版) 中指出, 如果泛型类型用 out 关键字标注, 泛型接口就是协变的. 这也就意味着返回类型只能是 T.
- /// <summary>
- /// 标识 out, 意味着返回类型只能是 T
- /// </summary>
- /// <typeparam name="T"></typeparam>
- interface Itest<out T>
- {
- T Tmain(object value);
- }
- public class Test : Itest<string>
- {
- public string Tmain(object value)
- {
- return value.ToString();
- }
- }
我们调用时:
- static void Main(string[] args)
- {
- Itest<string> itest = new Test();
- Itest<object> itestObj = itest;
- }
在这里, 我们最后接收其返回值的时候, 理应由 string 类型进行接收的, 但是这里我们可以修改, 由其基类 object 类型进行替换. 也就是在某个返回类型可以由其基类替换的时候, 也就是支持协变了. 注意其关键点. 返回类型, 由基类替换派生类.
然后我们再看看那抗变也可称为逆变. 在 C# 高级编程中指出的概念: 如果泛型类型用 in 关键字标注, 泛型接口就是抗变的. 这样, 接口只能把泛型类型 T 用作其方法的输入.
- /// <summary>
- /// 标识 in, 意味着输入类型只能是 T
- /// </summary>
- /// <typeparam name="T"></typeparam>
- interface Itest<in T>
- {
- string Tmain(T value);
- }
- public class Test : Itest<object>
- {
- public string Tmain(object value)
- {
- return value.ToString();
- }
- }
- class Program
- {
- static void Main(string[] args)
- {
- Itest<object> itest = new Test();
- Itest<string> itestStr= itest;
- }
- }
这里我们看上面这个例子, 其中返回类型已经是固定的 string 类型了. 而泛型接口中的泛型类型用来作为参数传递了. 我们再看调用时, 正常传入 object 类型的参数,, 但是我们修改传入参数类型为 string 类型也是可以的. 也就是我们在参入参数时, 参数可以由其派生类替换的话, 那么这个类也就是支持抗变 (逆变) 的. 注意其中关键点. 传入参数, 派生类替换基类.
总结
其实在上述例子及其概念中, 我们可以发现, 泛型接口的协变及抗变, 也就是将类型参数返回或者传入的情况, 在这情况下进行其类型的隐式转换所遵循的规律.
协变:(使用关键字 out)返回类型可以由其基类所替代的时候, 就是支持协变的.
抗变 (逆变):(使用关键字 in) 传入参数类型可以由其派生类所代替的时候, 就是支持抗变 (逆变) 的.
夫学须静也, 才须学也, 非学无以广才, 非志无以成学 ------- 诸葛亮
欢迎大家扫描下方二维码, 和我一起学习更多的 C# 知识
来源: https://www.cnblogs.com/hulizhong/p/11239743.html