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

在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稍微难理解一些。

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


猜你喜欢

转载自blog.csdn.net/zbbfb2001/article/details/78590496