【问题标题】:Why can't these generic types be inferred?为什么不能推断出这些泛型类型?
【发布时间】:2012-01-15 03:04:12
【问题描述】:

我有以下代码:

public static class CardView {
    public static object Column<TModel, TResult>(Func<TModel, TResult> field) {
        return null;
    }
}

public class Person 
{
    public string Name { get; set; }
    public bool Gender { get; set; }
}

void Main()
{
    var model = new Person() { Name = "Andre", Gender = true };

    var b = CardView.Column(model => model.Name); // ERROR
    // The type arguments for method 'UserQuery.CardView.Column<TModel,TResult>(System.Func<TModel,TResult>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
}

由于某种原因,它无法推断Column 方法的泛型类型。我需要知道为什么。我不能放弃类型推断并自己指定类型,因为这只是一个大问题的案例研究,它是必不可少的。

编辑

我拼错了代码 =/ 刚刚修复了它

【问题讨论】:

  • 你希望它如何推断TModel?提供的 lambda 对于具有 Name 属性的 任何 类将是“有效的”。
  • 您是否期望.Column 方法确定您的意思是m 代表model
  • 您能否更详细地解释一下依赖类型推断的原因,只是出于好奇?
  • @Damien_The_Unbeliever,我认为情况并非如此,因为您可以说:new List&lt;Person&gt;().Select(m =&gt; m.Name),它会在不依赖 Person 对象的情况下为您提供 `IEnumerable
  • 好吧,假设 you 只收到了您作为错误指出的声明。 you 如何推断出哪些类型参数对应于类型参数?如果做不到,你指望编译器怎么做呢?

标签: c# .net generics type-inference


【解决方案1】:

为了让编译器执行类型推断,它必须被赋予某种类型的信息。在这种情况下,它提供的唯一信息是一个无类型的 lambda 表达式。编译器在这里真的没什么可做的。

有几种方法可以解决此问题。最简单的方法是在 lambda 参数中添加一个类型

var b = CardView.Column((Person m) => m.Name);

【讨论】:

  • 换句话说,编译器不能将m变量的类型作为方法的通用参数TModelFunc的参数,对吧?
  • @IvayloSlavov 不可以,但它必须继续什么类型?这里没有可供查看的类型。只有 3 个无类型项:方法、lambda 和 lambda 参数。
  • 我的意思是,它没有什么可以连接两个类型参数来为它们使用相同的类型。如果我们有这个代码:public static object Column&lt;TModel, TResult&gt;(Func&lt;TModel, TResult&gt; field) 和这个电话:CardView.Column(m, p =&gt; p.Name); 问题就没有了。当然,Column 的代码应该将第一个参数传递给 lambda。
【解决方案2】:

该方法签名中没有TModel 参数(或使用TModel 的泛型类型),因此编译器不知道m 的类型。

【讨论】:

    【解决方案3】:

    技术上的答案是“因为规范没有说你可以做到”。引用the spec,第 7.5.2 节(类型推断):

    7.5.2.1 第一阶段

    对于每个方法参数 Ei:

    1. 如果 Ei 是匿名函数,则显式参数类型推断 (§7.5.2.7) 是从 Ei 到 Ti
    2. 否则,如果 Ei 具有类型 U 和 xi 是一个值参数,则从 U 到下界推断 钛。
    3. 否则,如果 Ei 具有 U 类型并且 xi 是 ref 或 out 参数 然后从 U 到 Ti 进行精确推断。
    4. 否则,不会对此参数做出任何推断。

    和:

    7.5.2.7 显式参数类型推断

    从表达式 E 到以下类型 T 的显式参数类型推断 方式:

    • 如果 E 是具有参数类型 U1…Uk 的显式类型匿名函数,并且 T 是具有参数的委托类型或表达式树类型 类型 V1...Vk 然后对每个 Ui 进行精确推断(第 7.5.2.8 节) 从 Ui 到对应的 Vi。

    由于您示例中的匿名函数没有显式键入,因此无法进行推断。这也告诉我们,要进行推断,您可以通过指定函数的类型来提供帮助:

    var b = CardView.Column((Person m) => m.Name); 
    

    现在这是明确的Func&lt;Person, string&gt;,因此推断成功。

    【讨论】:

      【解决方案4】:

      您编辑的代码无法编译。您不能使用 model 作为 lambda 参数,因为您已经将其声明为局部变量。您似乎想告诉编译器 lambda 参数是 Person,方法是使用该类型的已声明本地。但这行不通。

      告知编译器类型的正确方法是像其他人所说的那样在 lambda 中显式声明它:

      var b = CardView.Column((Person m) => m.Name); 
      

      或为泛型方法提供类型参数:

      var b = CardView.Column<Person, string>(m => m.Name); 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多