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

5

我不确定如何让这个问题更易读/易懂,但请听我说,希望在我们到达结尾时您能理解我的问题(至少,它很容易重现)。

我试图调用一个用于验证单元测试结果的方法。它具有以下签名:

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

这意味着该方法接受以下输入:
1. 任何可枚举的对象,其中包含与第二点输入相同类型的对象。 2. 一个 Func(通常封装 lambda 表达式),它接受与第 1 点“内容”类型相同的对象,并返回与第三点提供的数组内容类型相同的对象。 3. 一个由与第 2 点输出类型相同的对象组成的数组。
因此,该方法的实际执行可能如下所示:
AssertPropertyValues(
  item.ItemGroups, 
  itemGroup => itemGroup.Name, 
  "Name1", "Name2", "Name3");

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

因此,现在我被迫改用这种方式:

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

有人可以指出为什么编译器无法推断出这种情况吗?


1
你尝试过使用IEnumerable<TElement>或类似的东西来代替TEnumerable吗?基本上,propertyPointer参数应该与谓词(例如在Enumerable.Select扩展方法中)相同,因此整个结构工作方式相同... item.ItemGroups是哪种类型(是否有任何不匹配,使得显式签名成为必需?)?否则,我就无法理解你所面临的问题了... - user57508
我已经修复了您的代码示例的格式,使其合理,并重命名了您的问题;这与lambda无关。 - Eric Lippert
@AndreasNiedermair 我的问题源于我最初在多个地方都使用了这个约束,并且在某些地方将它们用作返回类型,因此无法仅使用接口来“应付”。但是尝试EricLippert的解决方案时不再是这种情况。 - Johny Skovdal
@EricLippert:难道不是第二个参数是一个lambda表达式导致了我的问题吗?如果我使用了委托,编译器就会有所有需要的信息。 - Johny Skovdal
1个回答

24

你的问题是由于约束条件不被视为类型签名的一部分,且在类型推断期间从未用于推导。你期望的推断过程如下:

  • 通过取第一个参数的类型来确定 TEnumerable
  • 通过从 TElement 中获取 IList<T> 实现信息来确定 TElement
  • 通过 lambda 表达式体的类型来确定 TProperty

但是C#不会执行第二个步骤,因为那需要考虑约束条件中的信息。正如你所提到的,如果你在 lambda 中提供该信息,则编译器将基于形式参数类型进行推导。

幸运的是,你的约束条件完全是不必要的。将方法重写为具有更简单签名的形式即可省去约束条件:

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

现在你应该没问题了。

同时,在这个过程中,除非出于某些特殊原因需要 IList<T>,否则您应该将其简化为 IEnumerable<TElement>


啊,我本来以为约束条件会被使用。有点遗憾它没有被用到。正如我在我的问题评论中指出的那样,一开始我无法简化它,因为其他方法依赖于返回类型的签名。经过一些重构,你的解决方案对我很有效。谢谢! :) (是的,我需要IList,但还是感谢你指出来) - Johny Skovdal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接