C#之泛型、协变、逆变

介绍

可以使用泛型声明的元素:类、接口、方法、委托
泛型出现之前使用object封装不同类型的参数,缺点:装箱拆箱性能差、运行时判断类型(不安全)...
泛型是在编译期间转为实际类型副本,所以性能好,还可以使用约束对泛型进行约束.

泛型约束

约束泛型类型必须满足约束。使用泛型约束后,可以使用约束类型的方式使用泛型变量

约束种类:

where T:class//引用类型约束
where T:new()//无参构造函数约束,加了此约束后可以使用无参构造函数创建实例:T t = new T();
where T:类//具体类或其子类约束
where T:接口//具体接口或其子类约束
where T:struct//值类型约束
where T:People,new(),ISport  //多个约束

协变逆变

协变逆变只能在接口或委托中使用
个人理解:协变逆变可以使我们的代码更灵活,帮我们做了类型转换操作。协变是子类转父类;逆变是父类转子类。

协变(out)

修饰返回值,泛型只能出现在成员的返回类型上,不能做传入参数

常用的支持协变的接口和委托有

  • IEnumerable

  • IEnumerator

  • IQueryable

  • IGrouping<out TKey, out TElement>

  • Func等共17个

  • Converter<in TInput, out TOutput>

协变案例

  public class Bird    {        public string ID { get; set; }    }    public class Sparrow : Bird    {        public string Name { get; set; }    }
static void Main(string[] args)        {            Bird bird = new Sparrow();//正确,一只麻雀就是一只鸟            List<Bird> birdList = null;            //birdList = new List<Sparrow>();//报错,一群麻雀和一群鸟没有继承关系            //只能用下面这两种方式            birdList = new List<Sparrow>().Select(s=>(Bird)s).ToList();//方式1            birdList = new List<Sparrow>().Cast<Bird>().ToList();//方式2            //上面这两种方法写起来比较麻烦,所以有了协变,协变就是解决这个问题 
            //为什么下面这个没有报错            IEnumerable<Bird> iBirdList = new List<Sparrow>();            //就是因为IEnumerable<out T>接口是支持协变的,协变使用out关键字          }

逆变(in)

逆变与协变相反,是父类转换子类,使用in关键字。泛型类型只能出现在传入参数上

常用支持逆变的接口和委托有:

  • IComparer

  • IComparable

  • IEqualityComparer

  • Action等共16个

  • Predicate

  • Comparison

  • Converter<in TInput, out TOutput>

逆变案例

    public interface ICustomerEnumerable<in T>    { }    public class CustomerEnumerable<T> : ICustomerEnumerable<T>   { }
  static void Main(string[] args){        //in 逆变       ICustomerEnumerable<Sparrow> sparrowList = new CustomerEnumerable<Bird>();   }