原文
01.协变性指的是——泛型类型参数可以从一个派生类隐式转化为基类(子类可以隐式的转换为父类)
例如string[] 可以转化为object[]
在C#4.0中引入out关键字来标记泛型参数支持协变性
// List<T>.AddRange(IEnumerable<T>)方法
//将其元素添加到 List<T> 的末尾的集合。 集合自身不能为 null
//但它可以包含为 null 的元素(如果类型 T 为引用类型)
//如果新 Count (当前 Count 加上集合的大小)将大于 CapacityList<T>
//则会自动重新分配内部数组以容纳新元素并将现有元素在添加新元素之前复制到新数组
List<int>list=new List<int>(){1,2,3};
List<int> newList=new List<int>(){5,6,7};
list.AddRange(newList);
foreach (var item in list)
{
WriteLine(item);
}
//1 2 3 5 6 7
static void Main(string[] args)
{
List<object> listObject = new List<object>();
List<string> listStr = new List<string>();
// AddRange方法接收的参数类型为IEnumerable<T> collection
// 下面的代码是传入的是List<string>类型的参数。
// 在MSDN中可以看出这个接口的定义为——IEnumerable<int T>。
// 所以 IEnumerable<T>泛型类型参数T支持协变性,所以可以
// 将List<string>转化为IEnumerable<string>(这个是继承的协变性支持的)
// 又因为这个IEnumerable<in T>接口委托支持协变性,所以可以把IEnumerable<string>转化为——>IEnumerable<object>类型。
// 所以编译器验证的时候就不会出现类型不能转化的错误了。
listObject.AddRange(listStr);
listStr.AddRange(listObject);
ReadKey();
}
代码中如果使用 这代码时 liststrs.AddRange(listobject); 就会出现编译时错误(无法从List转换为IEnumerable,因为List可以因为继承的协变性转化为IEnumerable,但是因为IEnumerable不支持逆变,即从object到string的转化,所以此时就会产生下面图中的错误
2.逆变
泛型类型参数可以从一个基类隐式转化为派生类
在C# 4.0中引入in关键字来标记泛型参数支持逆变性.为了更好的说明泛型的逆变性
class Program
{
static void Main(string[] args)
{
List<object> listobject = new List<object>();
List<string> liststrs = new List<string>();
IComparer<object> objComparer = new TestComparer();
IComparer<string> objComparer2 = new TestComparer();
// List<string>类型的 liststrs变量的sort方法接收的是IComparer<string>类型的参数
// 然而下面代码传入的是 IComparer<object>这个类型的参数,要编译成功的话,必须能够转化为IComparer<string>这个类型
// 正是因为IComparer<in T>泛型接口支持逆变,所以支持object转化为string类型
// 所以下面的这行代码可以编译通过,在.Net 4.0之前的版本肯定会编译错误,
// 大家可以把项目的目标框架改为.Net Framework 3.5或者更加低级的版本
// 这样下面这行代码就会出现编译错误,因为泛型的协变和逆变是C# 4.0 中新增加的特性,而.Net 4.0对应于C# 4.0。
liststrs.Sort(objComparer);
liststrs.Sort(objComparer2);
ReadKey();
}
}
public class TestComparer : IComparer<object>
{
public int Compare(object obj1, object obj2)
{
return obj1.ToString().CompareTo(obj2.ToString());
}
}