5

我不完全确定如何使这个问题可读/易于理解,但请听我说,我希望当我们结束时你能理解我的问题(至少,它很容易重现)。

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

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

这意味着,它需要以下输入

  1. 任何可枚举的对象,并且包含与 2) 的输入类型相同的对象。
  2. 一个 Func(通常封装 lambda 表达式),它接受一个与 1) 的“内容”相同类型的对象,并返回一个与 3) 中提供的数组内容的类型相同类型的对象。
  3. 与 2) 中的 Func 的输出具有相同类型的对象数组。

因此,此方法的实际执行可能如下所示:

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

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

所以现在我被迫这样做:

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

谁能指出为什么编译器无法推断出这种情况?

4

1 回答 1

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>出于某种原因需要。

于 2013-07-16T13:19:42.600 回答