具有多个参数的函数方差
Func variance with multiple parameters
在我们的代码中尝试过类似的操作,但失败了:
Func<Employee, Employee> _myFunc;
void Main()
{
Func<Employee, Employee> test1 = _myFunc;//Ok
Func<Employee, Person> test2 = _myFunc;//Ok
Func<Person, Employee> test3 = _myFunc;//Fails
Func<Person, Person> test4 = _myFunc;//Fails
}
public class Person { }
public class Employee : Person { }
后两种情况报错:
Cannot implicitly convert type System.Func<Employee, Employee>
to System.Func<Person, Employee>
. An explicit conversion exists (are you missing a cast?)
知道为什么吗?
Person
不是 Employee
Func<Employee, xxx>
和 Func<Person, xxx>
之间没有转换可能
如果您查看 Func<T, TResult>
的签名,您会看到输入参数(在本例中为 T
)是 逆变,并且return 类型 (TResult
) 是 协变
public delegate TResult Func<in T, out TResult>(T arg);
逆变基本上是关于能够将 "bigger" 类型传递给期望 "smaller" 类型的方法,其中协方差恰好相反。
Eric Lippert beautifully and elegantly (emphasis mine):
A generic type I is covariant (in T) if construction with reference
type arguments preserves the direction of assignment compatibility. It
is contravariant (in T) if it reverses the direction of assignment
compatibility. And it is invariant if it does neither. And by that, we
simply are saying in a concise way that the projection which takes a T
and produces I is a covariant/contravariant/invariant projection.
因为Func<T, TResult>
被定义为
public delegate TResult Func<in T, out TResult>(T arg);
如你所见,第二个参数(TResult
)确实是协变的,但是第一个参数(T
,它是函数的输入)实际上是逆变的(你只能用衍生较少的东西喂它。
Func<Employee, Person>
很好,因为它仍然匹配签名,而 Func<Person, Person>
失败,因为它不匹配。
见MSDN
好的,我想我现在明白了:
void Main()
{
Func<Employee, Employee> getEmployeesBoss = (Employee employee) => {return employee.Boss;};
//This works as it expects a Person to be returned and employee.Boss is a person.
Func<Employee, Person> getEmployeesBoss1 = getEmployeesBoss;
//This fails as I could pass a non Employee person to this func which would not work.
Func<Person, Employee> getEmployeesBoss2 = getEmployeesBoss;
}
class Person {}
class Employee : Person { public Employee Boss{get;set;} }
在我们的代码中尝试过类似的操作,但失败了:
Func<Employee, Employee> _myFunc;
void Main()
{
Func<Employee, Employee> test1 = _myFunc;//Ok
Func<Employee, Person> test2 = _myFunc;//Ok
Func<Person, Employee> test3 = _myFunc;//Fails
Func<Person, Person> test4 = _myFunc;//Fails
}
public class Person { }
public class Employee : Person { }
后两种情况报错:
Cannot implicitly convert type
System.Func<Employee, Employee>
toSystem.Func<Person, Employee>
. An explicit conversion exists (are you missing a cast?)
知道为什么吗?
Person
不是 Employee
Func<Employee, xxx>
和 Func<Person, xxx>
如果您查看 Func<T, TResult>
的签名,您会看到输入参数(在本例中为 T
)是 逆变,并且return 类型 (TResult
) 是 协变
public delegate TResult Func<in T, out TResult>(T arg);
逆变基本上是关于能够将 "bigger" 类型传递给期望 "smaller" 类型的方法,其中协方差恰好相反。
Eric Lippert beautifully and elegantly (emphasis mine):
A generic type I is covariant (in T) if construction with reference type arguments preserves the direction of assignment compatibility. It is contravariant (in T) if it reverses the direction of assignment compatibility. And it is invariant if it does neither. And by that, we simply are saying in a concise way that the projection which takes a T and produces I is a covariant/contravariant/invariant projection.
因为Func<T, TResult>
被定义为
public delegate TResult Func<in T, out TResult>(T arg);
如你所见,第二个参数(TResult
)确实是协变的,但是第一个参数(T
,它是函数的输入)实际上是逆变的(你只能用衍生较少的东西喂它。
Func<Employee, Person>
很好,因为它仍然匹配签名,而 Func<Person, Person>
失败,因为它不匹配。
见MSDN
好的,我想我现在明白了:
void Main()
{
Func<Employee, Employee> getEmployeesBoss = (Employee employee) => {return employee.Boss;};
//This works as it expects a Person to be returned and employee.Boss is a person.
Func<Employee, Person> getEmployeesBoss1 = getEmployeesBoss;
//This fails as I could pass a non Employee person to this func which would not work.
Func<Person, Employee> getEmployeesBoss2 = getEmployeesBoss;
}
class Person {}
class Employee : Person { public Employee Boss{get;set;} }