LINQ - FirstOrDefault() 然后 Select()

41

我有一个LINQ查询,当FirstOrDefault()返回null时会引发异常。理想情况下,我希望避免null检查。是否有办法做到这一点?如果没有符合FirstOrDefault()调用的CPOffsets,我希望返回0

double offset = OrderedOffsets.FirstOrDefault(o => o.OffsetDateTime > cpTime).CPOffset;

我认为实现这一点的唯一方法是:

CPOffset cpOffset = OrderedOffsets.FirstOrDefault(o => o.OffsetDateTime > cpTime);
double offset = cpOffset != null ? cpOffset.CPOffset : 0;

还有其他更简洁的方法吗?在 FirstOrDefault() 之后使用 Select() 无法编译,但我认为这可能是适当的?


1
这实际上是你能得到的最简洁的表述了...大多数情况下,“够好”就足够好了 :-) - theMayer
我同意。如果你想要更简洁的话,你可能需要编写自己的方法或扩展方法。(这并不一定是坏事)编辑:如果你想对谓词有更多的控制,那么 double offset = DetermineOffset(OrderedOffsets, cpTime); 或者 double offset = OrderedOffsets.DetermineOffset(cpTime) 或者 double offset = OrderedOffsets.DetermineOffset(o => o.OffsetDateTime > cpTime); 都没有问题。 - Chris Sinclair
请查看:http://stackoverflow.com/questions/14791514/streamlined-way-to-do-c-sharp-run-time-type-identification-that-avoids-a-null-ch/14791613#14791613 - Glenn Ferrie
3个回答

67

我认为这应该可以正常工作,但我不在附近的VS进行检查...

OrderedOffsets.Where(o => o.OffsetDateTime > cpTime).Select(x => x.CPOffset).FirstOrDefault();

1
@Servy - 如果所有的OffsetDateTime都小于cpTime,那么这是正确的吗? - Simon
@Servy,假设使用First就足够了。相比使用FirstOrDefault,它会更好多少呢?9个字符? - I4V
我写了自己的解决方案,不需要遍历整个集合(这就是使用 where 做的事情),以此来解决速度慢的问题。 - Felix K.
1
@felix-k - 不太明白你的意思;我认为这个解决方案不会遍历整个集合。Where是一个迭代器,只会在需要时被调用。FirstOrDefault将在找到第一个项目后停止。 - jdpilgrim
@jdpilgrim 你说得对,当时我不知道这个。但是把它放在一个单一的方法调用中还是更好,因为更容易阅读。 - Felix K.

14

DefaultIfEmpty可以用来确保集合始终至少有一个元素。

double offset = OrderedOffsets.Where(o => o.OffsetDateTime > cpTime)
    .Select(o => o.CPOffset)
    .DefaultIfEmpty()
    .First();

1
这与在接受的答案中在Select()之后仅调用FirstOrDefault()有何不同或更好? - JohnB
@JohnB 的主要优势在于能够提供默认值,而不需要是该类型的默认值。 - Servy

6
我认为一个好的模式可以是:

我认为一个好的模式可以是:

double offset = (OrderedOffsets.FirstOrDefault(o => o.OffsetDateTime > cpTime) ?? someDefaultObject).CPOffset;

使用someDefaultObject,一个持有默认值的对象... 使用这种模式,你可以通过代码轻松地更改默认值!

如果OrderedOffsets可以是一个结构体,你也可以将默认值直接放在其中!:)


1
好的,这个方法可以运行,但是你认为这比NSGaga的答案更好在哪里呢?它不需要一个临时对象“someDefaultObject”,并且更易读(当然这是主观的)。 - L.B
是的,但我不太喜欢我们在“Where”之后保留所有值> cpTime的方法,我更喜欢一种在找到第一个元素后返回的方法,如果列表非常大,我们真的想要投影所有传递给新表单的CPOffset吗?我认为这不值得为了节省一行而去做...我宁愿选择最初问题中的两行。 - Romain
1
我认为你应该更多地了解Linq和惰性求值。NSGaga的答案会在找到第一个匹配项后立即返回。如果它找不到匹配项,它将迭代整个列表,这也适用于你的答案。 - L.B
1
@Romain 在你的查询中,Where 将会获取与我的和 NSGaga 的查询完全相同次数的 OffsetDateTime,它将为每个元素获取,直到找到匹配项,或者如果没有匹配项则获取所有元素。Select 获取 CPOffset 的操作将仅应用于我和 Gaga 的答案中的最多一个项目,这是由于惰性评估和潜在的不完整迭代的 Select 所导致的。 - Servy
@Romain,问题基本上在于所有操作执行得非常快,以至于为Select创建状态机的简单开销是明显的时间。只需修改代码,使具有P = 2的项目添加到具有P = 99的10,000个项目之后而不是之前。然后,您会发现它不需要2倍的时间,它需要几乎完全相同的时间。添加Select仅增加了非常小的恒定时间量。当真正的工作与之相比微不足道时,这种数量才会引起注意,但通常情况并非如此。 - Servy
显示剩余10条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接