C#2.0开始引入协变和逆变,4.0开始引入泛型协变和逆变.
大家都知道子类和父类是可以直接相互转换的,但是泛型是不行的,因为涉及的类型安全问题:
List<string> strs = new List<string>();
List<object> objs = strs;
上面的代码报错,List<string>转换成List<object>失败.那是因为List<T>的类型参数不支持协变.
我们把上面的代码稍微改一下,如下:
List<string> strs = new List<string>();
IEnumerable<object> objs = strs;
不报错,List<string>转换成List<object>成功,那是因为IEnumerable<T>的类型参数支持协变.
这就引入了泛型协变和泛型逆变概念,便于在一定条件下,可以进行转换,,,,,,
如下demo展示了协变和逆变的具体用法:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 泛型的协变和逆变
{
class Program
{
//自定义一个支持逆变的泛型委托
public delegate void D_in<in T>(T t);
//自定义一个支持协变的泛型委托
public delegate T Del_out<out T>();
//static void M(object o) { }
static void Main(string[] args)
{
//1.变体包括协变和逆变,通过下面下面两句话,就能理解什么是协变和逆变了.
//协变:父类引用指向子类对象,out关键字
//public interface IEnumerable<out T> : IEnumerable
IEnumerable<object> list = new List<string>();
//逆变:子类引用指向父类对象,in关键字
//public delegate void Action<in T>(T obj);
Action<object> action_obj = o => {/*委托引用的方法的执行代码*/ };
Action<string> myAction = action_obj;
//2.数组的协变
//协变的类型只能是引用类型,不能是值类型
//如下,字符串数组支持协变
object[] objects = new string[10];
//但int[]不支持协变,因为int是值类型,如下语句报错不兼容.
//object[] objects = new int[10];
//如下数组变量类型为delegate,而object是一切类型的父类,如下语句协变成功.
Delegate[] delegates_del = new Action<int>[10];
object[] delegates_obj = new Action<int>[10];
//3.泛型的协变和逆变
//关键字:泛型,类型参数,out,in
//泛型委托和泛型接口支持协变和逆变
//在泛型委托中,协变只能用于返回类型,而逆变只能用于输入参数,如下泛型委托类型,T为输入参数,TResult为返回参数.
//public delegate void Action<in T>(T obj);
//public delegate TResult Func<in T, out TResult>(T arg);
//4.自定义Base,Derived类型,Derived类派生自Base类,在自定义Func<Derived, Base>委托中实现协变和逆变.
//自定义两个委托类型Del_out<out T>、D_in<in T>,分别支持协变/逆变,体现其自动转换
D_in<Base> d_base = M_in;
D_in<Derived> d_in_derived = d_base;
//或者写成一句代码也可以
D_in<Derived> d_in_derived1 = d_base;
//调用委托
d_in_derived(new Derived());
d_in_derived1(new Derived());
Del_out<Derived> d_out_derived = M_out;
Del_out<Base> d_out_base = d_out_derived;
//或者写成一句代码也可以
Del_out<Base> d_out_base1 = M_out;
//调用委托
Base bb = d_out_base1();
//5.//声明一个Func泛型委托Func<Derived, Base>,的输入参数类型是in Derived,返回参数类型out Base,体现协变和逆变的自动转换
Func<Derived, Base> myFunc = MyMethod;
//调用委托
Base result = myFunc(new Derived());
//上面的写法似乎有点绕,换成下面的写法就清晰了.
Func<Base, Derived> MyFunc_original = MyMethod;
Func<Derived, Base> myFunc_AutoChange = MyFunc_original;
//调用委托
Base res = myFunc_AutoChange(new Derived());
}
static Derived MyMethod(Base b) => b as Derived ?? new Derived();
static void M_in(Base b) { }
static Derived M_out() { return new Derived(); }
}
public class Base{}
public class Derived :Base{ }
}
.NET Framework 4 引入了对多个现有泛型接口的变体支持。 变体支持允许实现这些接口的类进行隐式转换,以下说明来源于微软的说明文档:
自 .NET Framework 4 起,以下接口为变体:
- IEnumerable<T>(T 是协变)
- IEnumerator<T>(T 是协变)
- IQueryable<T>(T 是协变)
- IGrouping<TKey,TElement>(
TKey
和TElement
都是协变) - IComparer<T>(T 是逆变)
- IEqualityComparer<T>(T 是逆变)
- IComparable<T>(T 是逆变)
自 .NET Framework 4.5 起,以下接口是变体:
- IReadOnlyList<T>(T 是协变)
demo里少了泛型接口的协变和逆变实例,以后补充吧~~~