为什么方法类型推断无法推断出类型参数?

时间:2013-07-16 13:10:22

标签: c# generics lambda

我不确定如何使这个问题可读/可理解,但听到我的意见,我希望你在我们结束时能理解我的问题(至少,它很容易重现)。

我尝试调用用于在UnitTests中验证结果的方法。它有以下签名:

void AssertPropertyValues<TEnumerable, TElement, TProperty>(
  TEnumerable enumerable, 
  Func<TElement, TProperty> propertyPointer, 
  params TProperty[] expectedValues) 
  where TEnumerable : System.Collections.Generic.IList<TElement>

这意味着它需要以下输入

  1. 任何可枚举的对象,并包含与2的intput相同类型的对象。
  2. 一个Func(通常封装lambda表达式),它接受与1)的“内容”相同类型的对象,并返回与3)中提供的数组内容类型相同类型的对象。 / LI>
  3. 与2)中Func输出相同类型的对象数组。
  4. 因此,此方法的实际执行方式如下:

    AssertPropertyValues(
      item.ItemGroups, 
      itemGroup => itemGroup.Name, 
      "Name1", "Name2", "Name3");
    

    至少,我希望它看起来像这样,但我遇到了众所周知的编译器错误:“方法'X'的类型参数无法从使用中推断出来。”,这就是我不明白。它应该包含我所能看到的所有信息,或者它可能是“协方差和反演”问题的另一个版本?

    所以现在我不得不这样做:

    AssertPropertyValues(
      item.ItemGroups, 
      (ItemGroup itemGroup) => itemGroup.Name, 
      "Name1", "Name2", "Name3");
    

    有人能指出编译器无法推断出这种情况的原因吗?

1 个答案:

答案 0 :(得分:24)

您的问题是由于约束不被视为签名的一部分而且永远不会用于在类型推断期间进行推断。您期望推断:

  • TEnumerable取决于第一个参数的类型。
  • TElement取决于IList<T>
  • 中的TElement实施信息
  • TProperty由lambda的正文类型决定

但是C#从未迈出第二步,因为这需要考虑来自约束的信息。如您所知,如果您在lambda中提供该信息,则编译器会根据形式参数类型进行推导。

幸运的是,你的约束是完全没必要的。重写您的方法以获得一个没有约束的简单签名:

void AssertPropertyValues<TElement, TProperty>(
  IList<TElement> sequence, 
  Func<TElement, TProperty> projection, 
  params TProperty[] expectedValues)

现在你应该没事。

虽然您正在使用它,但您应该将其简化为IEnumerable<TElement>,除非您出于某种原因需要IList<T>