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

问题描述:

我不完全确定如何使这个问题可读/可理解,但听到我的出来,我希望你能理解我的问题,当我们到最后(至少,它很容易再现)。为什么方法类型推断无法推断出类型参数?

我尝试调用一个方法来验证UnitTests中的结果。它具有以下特征:

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

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

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

因此,这种方法的实际执行可能看起来像这样:

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

至少,这是我想它的样子,但我碰上了众所周知的编译器错误: “方法'X'的类型参数不能从用法推断出来。”,这是我不明白的。它应该有我所能看到的所有信息,或者它是“协方差和逆变”问题的另一个版本?

所以现在我不得不做这样的代替:

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

任何人都可以指出为什么这种情况下不能由编译器推断?

+1

您是否尝试过使用'IEnumerable '或者......而不是'TEnumerable'?基本上''propertyPointer''参数应该与谓词相同,例如在'Enumerable.Select'扩展方法中(因此整个构造工作都一样)...... item.ItemGroups具有哪种类型(任何错配,这使得明确的签名是强制性的?)?否则我不会遇到你正面临的问题... –

+0

我已经修复了代码示例的格式,以便它们合理并重新命名为你的问题;这与lambda无关。 –

+0

@AndreasNiedermair我的问题源于我最初有这个约束多个地方,并在一些地方使用它们作为返回类型,因此不能只用接口“做”。在尝试EricLippert的解决方案时不再是这种情况。 –

您的问题是由于约束不被认为是签名的一部分,并且在类型推断过程中从不用于进行扣减。您期待的推理如下:

  • TEnumerable是通过采用第一个参数的类型来确定的。
  • TElement被采取从TElement
  • TPropertyIList<T>实现信息由拉姆达体的类型来确定确定

但是C#从来没有提过这第二个步骤,因为这需要从约束考虑信息。正如你注意到的那样,如果你在lambda中提供这些信息,那么编译器根据形式参数类型进行推导。

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

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

现在你应该没问题。

虽然你在这,但你应该简化到IEnumerable<TElement>,除非你需要一个IList<T>出于某种原因。

+0

啊,我实际上认为使用了约束。有点耻辱,它不是。正如我在对我的问题的评论中所指出的那样,我无法简化这个过程,因为依赖于其他需要返回类型签名的方法。经过一些重构,你的解决方案虽然为我工作。谢谢! :)(是的,我需要的IList,但感谢指出它反正) –