建议43:让接口中的泛型参数支持协变
除了上一建议中提到的使用泛型参数兼容接口不可变性外,还有一种办法是为接口中的泛型声明加上out关键字来支持协变,如下所示:
interface ISalary<out T> //使用out关键字 { void Pay(); }
static void Main(string[] args) { ISalary<Programmer> s = new BaseSalaryCounter<Programmer>(); ISalary<Manager> t = new BaseSalaryCounter<Manager>(); PrintSalary(s); PrintSalary(t); } static void PrintSalary(ISalary<Employee> s) //用法正确 { s.Pay(); }
这段代码在FCL4.0以前是不能编译通过的,因为IEnumerable<T>这个接口在FCL中没有被声明为IEnumerable<out T>:
static void Main() { IList<Programmer> programmers = new List<Programmer>(); IList<Manager> managers = new List<Manager>(); PrintPersonName(programmers); PrintPersonName(managers); } static void PrintPersonName(IEnumerable<Employee> persons) { foreach (Employee person in persons) { Console.WriteLine(person.Name); } }
FCL4.0对很多接口进行了修改以支持协变,如IEnumerable<out T>、IEnumerator<out T>、IQuerable<out T>等。由于IEnumerable<out T>现在支持协变,所以上段代码在FCL4.0中能运行得很好。
在我们自己的代码中,如果要编写泛型接口,除非确定该接口中的泛型参数不涉及变体,否则都建议加上out关键字。协变增大了接口的使用范围,而且几乎没有带来什么副作用。
转自:《编写高质量代码改善C#程序的157个建议》陆敏技