【问题标题】:Stop LINQ Query if Default Value is Returned at Any Point如果在任何时候返回默认值,则停止 LINQ 查询
【发布时间】:2013-08-14 15:36:02
【问题描述】:

当用流畅的语法编写时,是否有语法糖或方法来“短路”同一行中的 LINQ 语句?例如,如果 nullFirstOrDefault 调用返回,是否可以返回 null 否则继续方法链的其余部分?

List<string> testList = new List<string>() { "some", "strings" };
var reversed = testList.FirstOrDefault(x => x == "hello").Reverse();

FirstOrDefault 调用将返回 null,因此该语句将在 Reverse 调用上引发 ArgumentNullException。有没有办法在Reverse 调用之前停止,因为此时返回了默认值?

(我知道查询可以分成另一行,default(T) 可以在Reverse 之前检查,或者,对于简单的情况,可以使用条件运算符(?:),但我很好奇我是否可以'我错过了更长/更复杂链的特定功能)

编辑 - 澄清这只是一个展示想法的示例,它不是任何实际代码的一部分。我想避免抛出异常,因为只拆分行并进行检查会更高效。

【问题讨论】:

  • 这没有意义 - FirstOrDefault() 只会返回一个元素,所以 Reverse() 在这里没有意义......你想做什么?
  • 这只是一个不可否认的人为的例子。我实际上并没有这样做,或者特别是什么,但这只是一个应该返回 null 而不是继续沿着方法链向下的场景。

标签: c# linq default-value syntactic-sugar short-circuiting


【解决方案1】:

如果您想将集合解析为单个项目并操作该项目,但仍要优雅地处理可能在集合中找不到该项目的情况,您可以将FirstOrDefault 延迟到最后,像这样:

var reversed = testList
    .Where(x => x == "hello")
    .Select(x => x.Reverse())
    .FirstOrDefault();

或者等价

var reversed = testList
    .Where(x => x == "hello")
    .Select(Enumerable.Reverse)
    .FirstOrDefault();

如果列表中没有满足Where 方法中指定条件的项目,这将返回null。这里的性能差异最多可以忽略不计。与原始代码相比,它不需要循环遍历testList,因为一旦找到第一个元素,就会立即返回结果。

【讨论】:

  • 我认为 OP 只是举了一些例子来说明问题,我想他可能想再举一个例子,因为这个可以通过这个来解决(解决)。
  • @ReedCopsey 因为我们要反转字符串,而不是可枚举
  • 谢谢,我已经澄清了我的问题。 King King 是对的,这只是一个很容易解决的例子,但我对这个特定问题的实际解决方案不感兴趣。我想知道是否有一种语言功能,可以在任意点中途检测到默认值,然后停止 LINQ 查询的其余部分。
  • @keyboardP 这个解决方案就是这样做的。如果未找到任何项目,则不会执行 Reverse(或 .FirstOrDefault 之后的任何内容)。
  • 实际上,FirstOrDefault 正是这样做的。这就是为什么它比SingleOrDefault 性能更高。
【解决方案2】:

是的,只需使用 First 而不是 FirstOrDefault;它会提前抛出异常。

【讨论】:

  • 感谢您的回复,但这并不是我真正想要的避免异常的想法。如果要处理异常,则可以在 Reverse 调用中完成,或者最好从性能的角度来看,将查询拆分为多行。我应该在我的问题中澄清这一点。
  • 那么不幸的是,答案是否定的。您似乎在询问性能,从性能的角度来看,抛出的异常实际上是最佳的。如果您知道这确实是该块中错误的唯一原因,只需将 catch 完全留空,甚至不要定义 Exception 的类型并让它吃掉错误。
  • 澄清一下:拆分查询并执行 if/then 检查空值远不如仅仅吃异常。
  • @wilso132:我不同意。如果他期望它更频繁地被发现而不是被发现,或者“无结果”的比例相当高,那么异常可能会对性能产生影响。此外,Linq 通常不会被执行,直到它被枚举(即 foreach 循环)或转换为某些东西(.ToArray().ToList()
  • @Tseng 我理解你的论点,但我认为它不正确。您建议检查 .Any() (发出查询)然后执行另一个功能(在某些时候必须发出另一个查询)比处理甚至不使用引用的异常要密集得多。如果他预计会有很多个例外,那么你的观点是有道理的,但我认为它必须超过 50% 的情况。
【解决方案3】:

看起来您正在寻找可能的单子的 C# 版本。

public static class MaybeMonadExtensions
{
    public static TResult GetIfNotNull<TInput, TResult>(this TInput o, Func<TInput, TResult> evaluator)
        where TResult : class
        where TInput : class
    {
        return o == null ? null : evaluator(o);
    }
}

使用这个,你可以写

var reversed = testList.FirstOrDefault(x => x == "hello").GetIfNotNull(s => s.Reverse());

如果您的列表不包含单词“hello”,则将输出 null。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-12
    相关资源
    最近更新 更多