在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稍微难理解一些。
但我相信经过以上的实例和讲解,大家应该能深刻地理解了委托中的协变和逆变!