【问题标题】:Why does method type inference fail to infer a type parameter?为什么方法类型推断无法推断类型参数?
【发布时间】:2013-07-16 13:10:21
【问题描述】:

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

我尝试在 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");

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

【问题讨论】:

  • 您是否尝试过使用IEnumerable&lt;TElement&gt; 或类似的东西来代替TEnumerable?基本上propertyPointer-parameter 应该与谓词相同,例如Enumerable.Select-extension 方法中的谓词(因此整个构造工作相似)...item.ItemGroups 具有哪种类型(任何不匹配,这使得显式签名成为强制性?)?否则我不明白你面临的问题......
  • 我已经修复了您的代码示例的格式,以便它们合理并重命名您的问题;这与 lambda 无关。
  • @AndreasNiedermair 我的问题源于我最初在多个地方都有此约束,并且在某些地方将它们用作返回类型,因此不能仅使用接口“凑合”。尝试 EricLippert 的解决方案时不再是这种情况了。
  • @EricLippert:第二个参数是一个 lambda 表达式,这不是让我开始遇到问题的原因吗?如果我使用委托,编译器将拥有所需的所有信息。

标签: c# generics lambda


【解决方案1】:

您的问题是由于约束不被视为签名的一部分并且在类型推断期间从未用于进行推断的事实引起的。您期望推理进行:

  • TEnumerable 由第一个参数的类型决定。
  • TElement 是通过从TElement 获取IList&lt;T&gt; 实现信息来确定的
  • TProperty 由 lambda 主体的类型决定

但 C# 从未迈出第二步,因为这需要考虑来自约束的信息。正如您所注意到的,如果您在 lambda 中提供该信息,那么编译器会根据形参类型进行推断。

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

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

现在你应该没事了。

当您使用它时,您可能应该将其简化为IEnumerable&lt;TElement&gt;,除非您出于某种原因需要IList&lt;T&gt;

【讨论】:

  • 啊,其实我还以为用到了约束。有点遗憾,它不是。正如我在对我的问题的评论中指出的那样,我一开始就无法简化这一点,因为它依赖于需要返回类型签名的其他方法。经过一些重构后,您的解决方案对我有用。谢谢! :)(是的,我需要 IList,但无论如何都要感谢您指出)
猜你喜欢
  • 2022-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多