查看: 340|回复: 0

[ASP.NET教程] C#逆变

发表于 2017-5-14 09:21:04

在好多的.net的书籍中都看到过逆变和协变的概念,也在网上搜了一些关于这两个概念的解释,但是一直感觉似懂非懂的,直到最近在项目中实际遇到了一个问题,恰好用到了逆变,总算对逆变的理解又进了一步。

逆变只能用到泛型接口和委托中,以前一直不理解为什么要用在泛型中,今天终于想明白了。在介绍逆变之前,先来说说泛型,泛型的作用就是算法的重用,举个例子

  1. 1 public class EntityBase<T>
  2. 2 {
  3. 3 DbContext db = new DbContext();
  4. 4 public void Add(T Entity)
  5. 5 {
  6. 6 db.DbSet<T>.Add(Entity);
  7. 7 }
  8. 8 public void Remove(T Entity)
  9. 9 {
  10. 10 db.DbSet<T>.Remove(Entity);
  11. 11 }
  12. 12 }
复制代码

看着很熟悉吧,对头,这就是我们在EF中常用的代码,每个实体类(映射到数据中的表的类)都用这个泛型类来定义,只需要指定T的类型就可以了,如果没有使用这个泛型类,那么我们不得不为每个实体类都定义一遍这些方法,所有类中的方法的代码除了类型,其他都一模一样。所以泛型就是算法的重用,泛型只对编译器可见,.net运行时环境是不知道泛型的,因为在编译时编译器自动根据T的类型,在每个类型中都生成一次方法的代码。

泛型接口的逆变其实也是为了实现算法的重用,如下面的例子

  1. 1 public interface ICry<out T>
  2. 2 {
  3. 3 void Cry();
  4. 4 }
  5. 5 public class Animal
  6. 6 {
  7. 7
  8. 8 }
  9. 9 public class Cat : Animal, ICry<Cat>
  10. 10 {
  11. 11 public void Cry()
  12. 12 {
  13. 13 Console.WriteLine("喵喵喵");
  14. 14 }
  15. 15 }
  16. 16 public class Tiger : Animal, ICry<Tiger>
  17. 17 {
  18. 18 public void Cry()
  19. 19 {
  20. 20 Console.WriteLine("嗷嗷嗷");
  21. 21 }
  22. 22 }
  23. 23 public class Nibian
  24. 24 {
  25. 25 public void Metho1(ICry<Animal> an)
  26. 26 {
  27. 27 an.Cry();
  28. 28 }
  29. 29 }
复制代码

注意Nibian这个类Metho1方法,参数为ICry,我们却可以传ICry或ICry,就是因为我们在泛型接口中写了out这个逆变额关键字,虽然ICry和ICry并不是同一个类,而且它俩和ICry也不是父子类关系,按照OO原则,参数为父类,那么只能传父类和子类实例,如果没有逆变这个特性,我们还必须为每一个子类写一个这样方法,只是参数为ICry和ICry,哈哈,看出来了把,逆变就是为了实现算法重用。最后是主程序Main方法

  1. 1 static void Main(string[] args)
  2. 2 {
  3. 3 ICry<Cat> c = new Cat();
  4. 4 ICry<Tiger> t = new Tiger();
  5. 5 Nibian nb = new Nibian();
  6. 6 nb.Metho1(c);
  7. 7 nb.Metho1(t);
  8. 8 }
复制代码



回复

使用道具 举报