SingleOrDefault()在多个元素上抛出异常

Fra*_*dal 15 linq collections linq-to-sql

每当我拿这样的东西时,我都会收到异常

Feature f = o.Features.SingleOrDefault(e => e.LinkName == PageLink);
Run Code Online (Sandbox Code Playgroud)

因为这可以返回一个或多个元素.我可以用什么替代方法来解决这个问题?

spe*_*der 29

Single和SingleOrDefault设计为在序列中存在多于一个匹配时抛出.其结果是必须在完成之前迭代整个序列.听起来这不是你想要的.请尝试使用FirstOrDefault:

Feature f = o.Features
    .FirstOrDefault(e => e.vcr_LinkName == PageLink && e.bit_Activate == true);
Run Code Online (Sandbox Code Playgroud)

这将(通常)执行得更好,因为一旦找到匹配就完成.

当然,如果您确实想要保留多个元素,那么Where子句会更合适:

IEnumerable<Feature> fs = o.Features
    .Where(e => e.vcr_LinkName == PageLink && e.bit_Activate == true);
Run Code Online (Sandbox Code Playgroud)


Pie*_*kel 21

或者,如果你只想要一个匹配的项目,并且当有多个匹配时不想抛出,那么这很容易实现.我在我的项目中为此创建了一个扩展方法:

public static class QueryableExtensions
{
    public static TSource SingleWhenOnly<TSource>(this IQueryable<TSource> source)
    {
        if (source == null)
            throw new ArgumentNullException("source");

        var results = source.Take(2).ToArray();

        return results.Length == 1 ? results[0] : default(TSource);
    }

    public static TSource SingleWhenOnly<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
    {
        if (source == null)
            throw new ArgumentNullException("source");
        if (predicate == null)
            throw new ArgumentNullException("predicate");

        var results = source.Where(predicate).Take(2).ToArray();

        return results.Length == 1 ? results[0] : default(TSource);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你,我现在正在使用它.我将它重命名为`ExclusiveOrDefault`并且还创建了一个扩展方法`Exclusive`,当有*0*元素时抛出错误,但当有*2或更多*元素时返回null. (4认同)

Jon*_*eet 14

如果您只想要第一个元素,请FirstOrDefault改用.

基本上,以下是有效结果的选项(即您不想扔的地方)以及使用的内容:

  • 正好一个: Single
  • 一个或零: SingleOrDefault
  • 一个或多个: First
  • 零或更多: FirstOrDefault

(ElementAtElementAtOrDefault,Last并且LastOrDefault也可提供.)


Sea*_*ose 5

我发现如果不存在一个比正常SingleOrDefault行为更多的元素(即零个,两个或更多个),我需要返回默认值的行为,所以这是我的Pieter van Ginkel的改编版本:

public static class LinqExtensions
{
    /// <summary>
    /// Returns the only element of a sequence, or a default value if the sequence is empty or contains more than one element.
    /// </summary>
    public static TSource SingleOrDefaultIfMultiple<TSource>(this IEnumerable<TSource> source)
    {
        var elements = source.Take(2).ToArray();

        return (elements.Length == 1) ? elements[0] : default(TSource);
    }

    /// <summary>
    /// Returns the only element of a sequence, or a default value if the sequence is empty or contains more than one element.
    /// </summary>
    public static TSource SingleOrDefaultIfMultiple<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
        return source.Where(predicate).SingleOrDefaultIfMultiple();
    }

    /// <summary>
    /// Returns the only element of a sequence, or a default value if the sequence is empty or contains more than one element.
    /// </summary>
    public static TSource SingleOrDefaultIfMultiple<TSource>(this IQueryable<TSource> source)
    {
        var elements = source.Take(2).ToArray();

        return (elements.Length == 1) ? elements[0] : default(TSource);
    }

    /// <summary>
    /// Returns the only element of a sequence, or a default value if the sequence is empty or contains more than one element.
    /// </summary>
    public static TSource SingleOrDefaultIfMultiple<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
    {
        return source.Where(predicate).SingleOrDefaultIfMultiple();
    }
}
Run Code Online (Sandbox Code Playgroud)

我省略了null参数检查,因为在参数为null时我可以依靠Takeand Where调用引发异常,但是您可能会感到不舒服。

  • @Marcel我确实喜欢`OnlyOrDefault()`,但是我使用长名称的部分原因是,当我(或其他团队成员)开始输入`.SingleOrDefault`时,它将作为一种智能提示选项出现,我们希望最终选择一种适合这种情况的方式。 (3认同)
  • 或者,“ TheOneOrDefault”(我最近重新看过《黑客帝国》三部曲)怎么样? (2认同)