建议44:理解委托中的协变
委托中的泛型变量天然是部分支持协变的。为什么是“部分支持协变”?看下面示例:
class Program { public delegate T GetEmployeeHanlder<T>(string name); static void Main() { GetEmployeeHanlder<Employee> getAEmployee = GetAManager; Employee e = getAEmployee("Mike"); } static Manager GetAManager(string name) { Console.WriteLine("我是经理: " + name); return new Manager() { Name = name }; } static Employee GetAEmployee(string name) { Console.WriteLine("我是雇员: " + name); return new Employee() { Name = name }; } } interface ISalary<T> { void Pay(); } class BaseSalaryCounter<T> : ISalary<T> { public void Pay() { Console.WriteLine("Pay base salary"); } } class Employee { public string Name { get; set; } } class Programmer : Employee { } class Manager : Employee { }
上中的GetAManager返回的是一个Manager,但是在使用中,其实是将其赋值给一个泛型参数为Employee的委托变量。因为存在下面一种情况,所以编译不过:
GetEmployeeHanlder<Manager> getAManager = GetAManager;
GetEmployeeHanlder<Employee> getAEmployee = GetAManager;
要让上面的代码编译通过,同样需要为委托中的泛型参数指定out关键字:
public delegate T GetEmployeeHanlder<out T>(string name);
除非考虑到该委托声明肯定不会用于可变性,否则,为委托中的泛型参数指定out关键字将会拓展委托的应用,建议在实际编码过程中永远这样使用。实际上,FCL4.0中的一些委托声明已经用out关键字来让委托支持协变了,如我们常常会使用到的:
public delegate TResult Func<out TResult>(); public delegate TOutput Converter<in TInput, out TOutput>(TInput input);
转自:《编写高质量代码改善C#程序的157个建议》陆敏技