【问题标题】:Func variance with multiple parameters具有多个参数的函数方差
【发布时间】:2016-03-15 09:18:01
【问题描述】:

在我们的代码中尝试过这样的事情,但失败了:

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 { }

最后两种情况给出了这个错误:

无法将类型 System.Func&lt;Employee, Employee&gt; 隐式转换为 System.Func&lt;Person, Employee&gt;。存在显式转换(您是否缺少演员表?)

知道为什么吗?

【问题讨论】:

  • 因为返回类型(Func&lt;&gt; 的最后一个泛型参数)是协变的,而输入参数(Func&lt;&gt; 的所有其他泛型参数)是逆变的。
  • 有趣的事实:方法组到委托的转换也是协变和逆变的。如果您有Giraffe M(Animal a)delegate Animal D(Tiger t),那么D d = M; 在C# 中是合法的,即使D 甚至不是通用的。

标签: c# covariance func


【解决方案1】:

如果您查看Func&lt;T, TResult&gt; 的签名,您会看到输入参数(在本例中为T)是逆变的,而返回类型(TResult)是协变

public delegate TResult Func<in T, out TResult>(T arg);

逆变基本上是关于能够将“较大”类型传递给期望“较小”类型的方法,其中协方差正好相反。

Eric Lippert 把这个 beautifully and elegantly (emphasis mine):

如果使用引用构造,则泛型类型 I 是 协变(在 T 中) 类型参数保留分配兼容性的方向。它 是逆变(在T中),如果它反转分配方向 兼容性。如果它两者都没有,它就是不变。至此,我们 只是以简洁的方式说,采用 T 的投影 并产生 I 是协变/逆变/不变投影。

【讨论】:

  • Lippert 的原始帖子中有一个错字。协变应该是out T
  • @haim770 我不认为这就是埃里克的意思。我假设他的意思是泛型类型I&lt;T&gt; 是协变/逆变in T,这意味着每个T
  • 是的。我读了整篇文章,现在我明白他的意思了。谢谢
  • 这就是我的意思,但我同意我写它的方式非常混乱!很遗憾,我不再拥有对 msdn 博客的写入权限。
【解决方案2】:

因为Func&lt;T, TResult&gt; 被定义为

public delegate TResult Func<in T, out TResult>(T arg);

如你所见,第二个参数(TResult)确实是一个协变的,但第一个参数(T,也就是函数的输入)实际上是一个逆变(你只能给它喂东西)那是较少派生的)。

Func&lt;Employee, Person&gt; 很好,因为它与签名匹配,而Func&lt;Person, Person&gt; 失败,因为它不匹配。

MSDN

【讨论】:

    【解决方案3】:

    好的,我想我现在明白了:

    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;}    }
    

    【讨论】:

      【解决方案4】:

      Person 不是 Employee

      Func&lt;Employee, xxx&gt;Func&lt;Person, xxx&gt; 之间不能进行转换

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-09-23
        • 1970-01-01
        • 2020-08-29
        • 1970-01-01
        • 2013-08-02
        • 2015-04-25
        相关资源
        最近更新 更多