【问题标题】:How can I get the next appropriate value from a generic list using LINQ?如何使用 LINQ 从通用列表中获取下一个适当的值?
【发布时间】:2016-02-18 05:31:43
【问题描述】:

我有一堂课:

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

...以及该类的通用列表:

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);
}

在“英语”中,我想说的是,“给我第一个比传入的 arg (LastCounselPoint) 更大的数字并且 BR 为真的情况” p>

例如,如果我传入一个 17,并且 CounselNum val 为 18 的 CounselPoint“记录”也将 BR 设置为 true,它应该返回 18。简单,不是吗?

但我表面上看起来很丑陋,或者充其量是尴尬的尝试,甚至无法编译;我明白了:

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

...和:

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

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

如果我把代码改成这样:

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

我明白了:

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

...这也只会导致我告诉编译器,“除了九个,其他都下来了,pard;把它们放在另一条小巷里。

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

【问题讨论】:

  • 查看添加到我的答案中的改进...

标签: c# linq generics lambda generic-list


【解决方案1】:

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

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

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

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

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

【讨论】:

  • 我仍然收到前两个编译错误消息(CS0029 和 CS1662)
  • @B.ClayShannon 你完全正确,我必须更仔细地阅读错误消息。更新了答案。
【解决方案2】:

我在您的示例(以及其他 anwsers)中看到您已经编写了这样的 first-default:.FirstOrDefault(i =&gt; i.CounselNum) 但我认为您不需要这个。

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

所以你需要这样写

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

*更新>存在

正如@Jannik 指出的那样,当没有匹配项时,上面会返回零而不是 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;

请注意,您的方法的返回类型必须是可为空的 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

【讨论】:

  • 另一个注意事项:如果列表中没有适当的条目,这将返回 0
  • @Jannik 已更新 - 也在考虑订单,因为这似乎是下一个合乎逻辑的步骤
【解决方案3】:

我认为 dotnetom 和 Chris Moutray 已经回答了这个问题,我只想补充一点:

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

【讨论】:

    【解决方案4】:

    试试这个....

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

    【讨论】:

    • 我明白了:严重性代码描述项目文件行抑制状态错误 CS0029 无法将类型“AYttFMScheduler.CounselPoints”隐式转换为“int”
    猜你喜欢
    • 2015-12-28
    • 2011-10-13
    • 1970-01-01
    • 2021-01-08
    • 1970-01-01
    • 1970-01-01
    • 2019-03-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多