Linq避免两次调用函数

本文关键字:两次 调用 函数 Linq | 更新日期: 2023-09-27 17:57:59

使用Linq to Objects,查询可能需要根据函数的结果进行筛选并返回该函数的值。

例如

files.Where(x => string.IsNullOrWhiteSpace(x.getProperty(propName)))
                .GroupBy(x => x.getProperty(propName));

编译器是否认识到分组需要该值并将其保留?

如果没有,那么必须有一种方法来选择匿名类型,并根据该类型查询Where和GroupBy语句。是否可以使用匿名类型执行此操作?

我可以声明一个类并使用它。

class fileSelector
{
    internal string prop;
    internal myFile file;
}
var groups = files
     .Select(x => new fileSelector() { prop = x.getProperty(propName),  file = x })
     .Where(x => !string.IsNullOrWhiteSpace(x.prop))
     .GroupBy(x => x.prop);

但是,有没有一种方法可以用匿名类型实现这一点?

这就是我尝试的匿名类型

var groups = files.Select(x => new { x.getProperty(propName),  x })
                  .Where(x => !string.IsNullOrWhiteSpace(x.prop))
                  .GroupBy(x => x.prop);

但这给出了错误

无效的匿名类型成员声明符。匿名类型成员必须使用成员分配、简单名称或成员访问权限声明。

最终答案

var groups = files
     .Select(x => new { prop = x.getProperty(propName),  file = x })
     .Where(x => !string.IsNullOrWhiteSpace(x.prop))
     .GroupBy(x => x.prop, x => x.file);

Linq避免两次调用函数

编译器是否认识到分组需要该值并将其保留?

不,因为getProperty可能有预期的副作用。

如果没有,那么必须有一种方法来选择匿名类型,并根据该类型查询Where和GroupBy语句。是否可以使用匿名类型执行此操作?

是的。只需将new fileSelector() {...}替换为new {...},代码就可以正常工作。不过,请注意,在您的代码中(以及在使用匿名类型的修改版本中),分组的元素是fileSelector和匿名类型,而不是myFile。请参阅Scott Chamberlain的解决方案,了解如何解决此问题。

或者,您可以使用let子句来存储中间值:

var groups = from file in files
             let prop = file.getProperty(propName)
             where !string.IsNullOrWhiteSpace(prop)
             group file by prop;

编译器是否认识到分组需要该值并将其保留?

不,它会碰到两次数值。

实际上,与最后的示例非常接近,您可以使用annonamous类型,只需为匿名类型的每个成员提供名称,然后添加一个元素选择器,使分组的主体成为file属性。

var groups = files
     .Select(x => new { prop = x.getProperty(propName), file = x })
     .Where(x => !string.IsNullOrWhiteSpace(x.prop))
     .GroupBy(x => x.prop, x => x.file);