【问题标题】:LINQ Select - CS0411 The type arguments cannot be inferred from the usage - What am I doing wrong?LINQ Select - CS0411 无法从用法推断类型参数 - 我做错了什么?
【发布时间】:2017-10-23 12:03:21
【问题描述】:

目标: ForEach 扩展掩码选择。

原因: Select 允许方法链接,而 foreach 不允许; ForEach 比 Select 更具可读性。

我遇到的问题是我收到了这个错误。

无法从用法中推断方法“System.Linq.Enumerable.Select(System.Collections.Generic.IEnumerable, System.Func)”的类型参数。尝试明确指定类型参数。 (CS0411)

public static void ForEach<T>(this IEnumerable<T> elements, Action<T, int> action)
{
    elements.Select((elem, i) => action(elem, i));
}

我尝试过elements.Select((T elem, int i) =&gt; action(elem, i));,但这会产生同样的错误。

我也尝试过使用 Func 代替 Action,但似乎没有任何方法可以使用 void 作为返回值(无论如何,这就是 Action 的用途)。如果我将方法定义为采用 Func 而不是 Action,那么当我尝试使用 Action 调用它时,我会得到同样的错误。

public static IEnumerable<TOut> ForEach<TIn, TOut>(this IEnumerable<TIn> elements, 
    Func<TIn, int, TOut> function)
{
    return elements.Select((elem, i) => function(elem, i))
        .ToList();
}

我不明白哪些参数无法推断。有一些方法可以解决这个问题(使用 for 循环或 foreach 循环并跟踪索引),但我仍然想弄清楚为什么这不起作用。

编辑:当我从 foreach 循环切换到调用 Select 时,我还刚刚意识到我的另一个 ForEach for Action(无索引)也出现此错误。

public static void ForEach<T>(this IEnumerable<T> elements, Action<T> action)
{
    elements.ToList().Select((T elem) => action(elem));
    /*foreach (var elem in elements)
        action(elem);*/
}

【问题讨论】:

  • 为什么要使用Select?如果你想写ForEach,只需使用foreach 语句...
  • 请注意,如果您的第一个方法已编译,由于延迟评估,它实际上不会做任何事情。
  • 你不明白任何的错误吗?你明白Select 的意思是进行投影,从一种类型转到另一种类型吗?鉴于 action(elem)action(elem, i) 不返回任何内容,您希望编译器在这里推断什么?
  • 如果您只想弄清楚为什么它不起作用,那么 that 应该是既定目标 - 因为在顶部的目标的最佳答案问题是“使用 foreach 循环”(在您的 ForEach 实现中)。
  • @BlakeThingstad IEnumerable 是什么样的?

标签: c# linq


【解决方案1】:

问题在于Select 需要一个返回值(您将一个对象投影到其他东西中)- 其中您的Action 正在返回voidvoid 无法转换为 T,这是 Select 所期望的。

简单的解决方法是确保在您的选择函数结束时返回一些内容:

public static void ForEach<T>(this IEnumerable<T> elements, Action<T, int> action)
{
    elements.Select((elem, i) =>
    {
        action(elem, i);
        return elem;
    }).Count();
}

动作被调用,集合继续被枚举。我们需要调用Count,因为在我们尝试从每个枚举中获取值之前,实际上什么都不会发生(感谢 Jon Skeet!)

请注意,这不是 Select 的使用方式 - OP 专门询问了为什么/如何使其与它一起使用。

如 cmets 中所述,要正确执行此操作,解决方案是使用带有计数器的实际 foreach 循环:

public static void ForEach<T>(this IEnumerable<T> elements, Action<T, int> action)
{
    int count = 0;
    foreach (var item in elements)
    {
        action(item, count++);
    }
}

如果你想允许方法链接,你可以这样做:

public static IEnumerable<T> ForEach<T>(this IEnumerable<T> elements, Action<T, int> action) 
{ 
    int count = 0; 
    foreach (var item in elements) 
    {
        action(item, count++); 
        yield return item; 
    }
}

【讨论】:

  • 除非你调用它时实际上不会做任何事情。使用看起来可能有用但实际上没有用的方法给出答案 IMO。
  • @JonSkeet 啊!好点,“修复”它...我将添加一个注释来解释为什么我们需要 ToList。
  • 那会毫无意义地制造垃圾。 Count() 会更有效率,但从根本上说,这整件事是个坏主意。
  • @JonSkeet 我从来没想过,感谢您向我展示了这个技巧。我添加了一个正确解决方案的示例。很抱歉让您失望了:(
  • @Scott 关于 OP (Select allows for method chaining while foreach does not;),我想他会喜欢这样的:public static IEnumerable&lt;T&gt; ForEach&lt;T&gt;(this IEnumerable&lt;T&gt; elements, Action&lt;T, int&gt; action) { int count = 0; foreach (var item in elements) { action(item, count++); yield return item; } }
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多