在每次返回之前使用Where()从多个源返回第一个项

本文关键字:返回 第一个 Where | 更新日期: 2023-09-27 18:27:50

我发现了一个有趣的代码片段,它以非预期的方式使用Where()和foreach。然而,它们的使用似乎是一个非常有趣的解决方案,可以显著减少代码行数。

// a, b, c are IEnumerable<string> of any data source and could be expensive to iterate
string GetFirstString(){
    foreach( var str in a.Where(...) )
        return str;
    foreach( var str in b.Where(...) )
        return str;
    foreach( var str in c.Where(...) )
        return str;
    return "";
} 

有没有更好的方法可以做到这一点,既短又不"滥用"Where和foreach?

更新:

如果任何列表包含null条目,并且Where谓词允许null条目通过,那么它将返回null,这可能会有一个错误。(在大多数情况下,GetFirstString应该跳过空值)

在每次返回之前使用Where()从多个源返回第一个项

试试这个:

string GetFirstString(){
    return a.FirstOrDefault(...) 
        ?? b.FirstOrDefault(...) 
        ?? c.FirstOrDefault(...)
        ?? "";
}
string GetFirstString(){
    var str = "";
    if( str == null )
        str = b.FirstOrDefault(...);
    if( str == null )
        str = b.FirstOrDefault(...);
    if( str == null )
        str = c.FirstOrDefault(...);
    return str;
} 

这在我看来有点难看,因为它引入了一个变量并改变了方法的整个结构。

您可以使用Concat将所有内容连接到一个序列中,然后选择First

var list = a.Where(...)
           .Concat(b.Where(...))
           .Concat(c.Where(..))
           .Take(1)
           .ToList();//Make sure we execute the query only once.
if(list.Count > 0)
    return list[0];
return "";

注意:您可能认为Take(1)ToList和从列表中返回第一个元素似乎是多余的,您可以将其更改为FirstOrDefault。但这改变了代码的语义。问题是,当过滤后的序列包含null时,FirstOrDefault() ?? ""将返回空字符串,但OP的原始代码将返回null

使用FirstOrDefault()而不是First()

string GetFirstString(){
        if (a.Any(...))
           return a.FirstOrDefault(...);
        if (b.Any(...))
           return b.FirstOrDefault(...);
        if (c.Any(...))
           return c.FirstOrDefault(...);
        return "";
    } 

您可以使用Union扩展方法

string GetFirstString()
{
    return a.Where(...)
        .Union(b.Where(...))
        .Union(c.Where(...)).FirstOrDefault();
}