Delegate(委托)中的Covariance(协变)和Contravariance(逆变)

来源:互联网 发布:淘宝网身份认证复核 编辑:程序博客网 时间:2024/05/17 11:04

在Delegate当中,Variance(变异)是比较简单的理念,但不能深入理解的话,还是感觉比较疑惑的。


Covariance(协变)

  • Return types are compatible
  • A delegate variable can be used with a method that retures a type that is derived from the delegate's returned type
协变是用在方法返回值上的技术。

一个委托变量可以被赋值于一个方法,即使这个方法的返回值是继承于委托所定义时的返回值。


代码示例:

public class Person { } public class Employee : Person { } class Program{    static void Main(string[] args)    {        Func<Person> personMaker;        personMaker = MakePerson;        personMaker = MakeEmployee;         Func<Employee> employeeMaker;        //employeeMaker = MakePerson(); //这个是不能通过编译的        //employeeMaker = MakePerson2(); //这个是不能通过编译的        employeeMaker = MakeEmployee;    }     private static Person MakePerson() { return new Person(); }     private static Person MakePerson2() { return new Employee(); }     private static Employee MakeEmployee() { return new Employee(); }}


在以上示例中,

  • Func<Person>很显然可以被赋值于一个返回Person的方法。但如果一个方法返回Employee呢?因为Employee是继承自Person,Employee就是一个Person,所以即使是返回Employee的方法也是可以赋给Func<Person>的。这里就是Covariance所支持的理念。
  • Func<Employee>很显然可以被赋值于一个返回Employee的方法。但如果一个方法返回Person呢?因为Person不是Employee,即使Person实际上指的是Employee,这样的赋值也是不能通过编译的。
Covariance相对于Contravariance还是好理解一些。


Contravariance(逆变)

  • Parameter types are compatible
  • A delegate variable can be used with a method that takes parameters that are ancestors of the delegate's parameter types
逆变是用在方法参数上的技术。

一个委托变量可以被赋值于一个方法,即使这个方法的参数是委托所定义时的参数的父类。


代码示例:

public class Person { } public class Employee : Person { } class Program{    static void Main(string[] args)    {        Action<Person> personProcessor;        personProcessor = ProcessPerson;        //personProcessor = ProcessEmployee; //这个是不能通过编译的        personProcessor(new Person());        personProcessor(new Employee());         Action<Employee> employeeProcessor;        employeeProcessor = ProcessPerson;        employeeProcessor = ProcessEmployee;        //employeeProcessor(new Person()); //这个是不能通过编译的        employeeProcessor(new Employee());    }     private static void ProcessPerson(Person person) { }     private static void ProcessEmployee(Employee employee) { }}

在以上示例中,

  • Action<Person>在最终调用的时候必须是Person或它的子类。因为Employee也是Person,所以personProcessor可以传入Person或者Employee。
  • Action<Employee>在最终调用的时候必须是Employee或它的子类。传入Person的时候,那就不能通过编译。即使Person实际上代表的是Employee,也不能通过编译。
  • 为什么方法ProcessEmployee不能赋于personProcessor?理由很简单:ProcessEmployee处理的是Employee,但personProcessor只能处理Person。把一个Person传给ProcessEmployee,这是不行的。
  • 为什么ProcessPerson能赋值给employeeProcessor?因为ProcessPerson是处理Person的,但Employee也是Person,所以从概念上讲,但我们把ProcessPerson赋给employeeProcessor的时候,也就是准备处理Person,当employeeProcessor最终调用的时候传入的是Employee,所以应该完全可以。这里就是Contravariance所实现的理念。
  • 简言之,Action<Person>最终是处理Person的,ProcessPerson可以处理Person,但ProcessEmployee不可以处理Person。Action<Employee>最终是处理Employee的,ProcessPerson和ProcessEmployee是可以处理Employee的。
Contravariance相对于Covariance稍微难理解一些。

但我相信经过以上的实例和讲解,大家应该能深刻地理解了委托中的协变和逆变!


原创粉丝点击