如何使用 LINQ 从通用列表中获取下一个合适的值?

How can I get the next appropriate value from a generic list using LINQ?

我有一个 class:

public class CounselPoints
{
    public int CounselNum { get; set; }
    public String CounselPoint { get; set; }
    public bool BR { get; set; }
    public bool ICRVBS { get; set; }
}

...以及 class 的通用列表:

List<CounselPoints> counselPoints = AYttFMConstsAndUtils.DeserializeCounselPointsFile();

我想从通用列表中检索下一个合适的 CounselPoint,其逻辑可以用 SQL 表示,如下所示:

SELECT TOP 1 CounselNumber
FROM COUNSELPOINTSLU
WHERE BR IS TRUE
AND CounselNumber > @LastCounselPoint;

我调用此方法来尝试获取该值,并且像这样挥舞着它:

public static int GetNextBibleReadingCounselPoint(int LastCounselPoint)
{
    List<CounselPoints> counselPoints = AYttFMConstsAndUtils.DeserializeCounselPointsFile();
    return counselPoints.FirstOrDefault(i => i.CounselNum).Where(j => j.BR).Where(k => k.CounselNum > LastCounselPoint);
}

在 "English," 我想说的是,“给我第一个 CounselNum,它比传入的 arg (LastCounselPoint) 的数字更大,并且 BR 为真"

例如,如果我传入 17,并且 CounselNum val 为 18 的 counsePoint "record" 也将 BR 设置为 true,则它应该 return 18。很简单,不是吗?

但是我的尝试显然很丑陋,或者充其量是笨拙的,甚至没有编译;我得到:

错误 CS0029 无法将类型 'int' 隐式转换为 'bool'

...和:

错误 CS1662 无法将 lambda 表达式转换为预期的委托类型,因为块中的某些 return 类型无法隐式转换为委托 return 类型

我不知道为什么它认为我正在尝试转换为 bool,我绝对不会理解第二个错误。

如果我将代码更改为:

return counselPoints.FirstOrDefault(i => i.CounselNum > LastCounselPoint).Where(j => j.BR).Select(k => k.CounselNum );

我得到:

错误 CS1061 'CounselPoints' 不包含 'Where' 的定义并且没有扩展方法 'Where' 接受类型 'CounselPoints' 的第一个参数可以是找到(您是否缺少 using 指令或程序集引用?)

...这也只会导致我告诉编译器,“除了九点,其他都倒下了;把它们放在另一条胡同上。

解开这个难题的关键是什么?

使用查询时,首先需要使用过滤条件,然后才调用FirstOrDefault:

return counselPoints
    .Where(j => j.BR)
    .Where(k => k.CounselNum > LastCounselPoint)
    .Select(i => i.CounselNum)
    .FirstOrDefault();

有 2 种类型的 LINQ 查询运算符 - immediate and deferredWhere 是延迟运算符的例子,FirstOrDefault 是立即运算符。

在您的代码中,您在无序列表上使用了 FirstOrDefault,因此您立即过滤了初始列表。

在我提供的示例代码中,我首先使用延迟运算符定义过滤器,然后才使用即时函数检索结果。

试试这个....

counselPoints
    .Where(j => j.BR && j.CounselNum > LastCounselPoint)
    .FirstOrDefault();

我认为 dotnetom 和 Chris Moutray 已经回答了这个问题,我只想做一个小的旁注:

下次您遇到 LINQ 语句问题时,请尝试将它们拆分为单独的语句。这样做你会发现 FirstOrDefault() 在 Where() 之前没有任何意义,因为它会 return 一个单一的对象,你不能在单一对象上使用 where,对吗? :)

我在你的例子(以及其他答案)中看到你写的 first-default 是这样的:.FirstOrDefault(i => i.CounselNum) 但我认为你不需要这个。

将第一个默认值视为过滤器而不是 select。

所以你需要这样写

return counselPoints
  .Where(cp => cp.BR && cp.CounselNum > LastCounselPoint)  // where clause
  .Select(cp => cp.CounselNum) // select part
  .FirstOrDefault(); // get top 1 only

*更新 > 存在

正如@Jannik 指出的那样,当没有匹配项时,returns 为零而不是 null。

如果同时测试存在性很重要,那么你可以这样写。

CounselPoint cp = counselPoints
  .Where(cp => cp.BR && cp.CounselNum > LastCounselPoint)  // where clause
  .FirstOrDefault(); // get top 1 object of type CounselPoint

if (cp == null)
  return null;

return cp.CounselNum;

请注意,您的方法的 return 类型必须是可为 null 的 int。请参阅下面添加的 ?

public static int? GetNextBibleReadingCounselPoint(int LastCounselPoint)

...然后我猜你调用代码来检查结果

int? result = GetNextBibleReadingCounselPoint(lastCounselPoint);

if (result.HasValue) {
  // something was returned
} else {
  // nothing was returned
  int num = result.Value; 
}

*update2 > 订单

想着你的原创SQL - 不知道你是不是考虑下订单了??

也许你应该在底部添加一个订单:

SELECT TOP 1 CounselNumber
FROM COUNSELPOINTSLU
WHERE BR IS TRUE
AND CounselNumber > @LastCounselPoint
ORDER BY CounselNumber; // ascending

在这种情况下,您可以在 linq 语句中的 where 子句之前添加 order-by;像这样

return counselPoints
  .OrderBy(cp => cp.CounselNum) // order by ascending
  .Where( ... rest of code