在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,这样的赋值也是不能通过编译的。
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的。
但我相信经过以上的实例和讲解,大家应该能深刻地理解了委托中的协变和逆变!