【问题标题】:Unexpected result with Where()Where() 的意外结果
【发布时间】:2017-07-11 07:48:18
【问题描述】:

我有这段代码,它应该返回一个值类型,在每一步应用steps中指定的转换。

private static T Transformed<T>(T x, params Func<T, T>[] steps) where T : struct
{
     if ((steps?.Length ?? 0) == 0)
     {
        return x;
     }

     var reallyEmpty = steps.Where(f => (x = f(x)).Equals(int.MinValue));
     return x;
}

我只需要Where 扩展来完成每一步而不使用循环,因此我使用了一个可能永远不会为真的条件(Equals(int.MinValue))。 但如果我有这个调用代码,我会得到5 而不是15,这是我所期望的。

int res1 = Transformed(5, x => x * 2, x => x + 5);
Console.WriteLine(res1);

我的问题是为什么? Where 不是遍历每个元素并检查它吗?

【问题讨论】:

  • 你没有迭代 reallyEmpty 所以副作用 x = f(x) 永远不会执行。
  • 如果你是 linq 的忠实粉丝,你可以创建 ForEach 扩展,再也不会让自己或他人感到困惑。

标签: c# linq


【解决方案1】:

Where 被延迟评估 - 你永远不会使用它的结果,所以谓词永远不会被评估。

可以通过计算结果或类似结果来强制迭代:

var ignored  steps.Where(f => (x = f(x)).Equals(int.MinValue)).Count();

...但是循环自己会更清楚:

foreach (var step in steps) 
{
    x = step(x);
}

毕竟,您并没有真的通过使用 Where 来避免循环 - 您只是在隐藏它,这样做会使您的代码过于复杂,以至于您不需要不明白了。

【讨论】:

    【解决方案2】:

    如果您真的打算使用 LINQ,则可以使用 Aggregate 做您想做的事情:

    private static T Transformed<T>( T x, params Func<T, T>[] steps ) where T : struct
    {
        return steps?.Aggregate( x, ( accum, f ) => f( accum ) ) ?? x;
    }
    

    我通常不觉得聚合特别可读,但我认为值得一提。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-19
      • 1970-01-01
      • 1970-01-01
      • 2011-09-15
      相关资源
      最近更新 更多