如何获得IEnumerable的第一个值,如果长度恰好为1,或者默认情况下为其他值?

问题描述:

我想要的东西像IEnumerable.FirstOrDefault,但我想如果length != 1返回的默认值。类似于v.Count() == 1 ? v[0] : default。但我宁愿这样做,而不将整个枚举转换为数组。得到这种行为的最好方法是什么?如何获得IEnumerable的第一个值,如果长度恰好为1,或者默认情况下为其他值?

避免迭代枚举不止一次是个好主意,因为并非所有枚举都重复相同的值。

为了确保您的枚举只返回1个元素,从enumerable中获取前两个元素并将结果转换为数组是最有效的。如果数组有两个元素,那么您知道枚举具有多个元素,而不会遍历整个枚举。一个枚举可能无限长。

因此,这里是如何做到这一点:

public static T OneOnlyThenFirstOrDefault<T>(this IEnumerable<T> source) 
{ 
    var beginning = source.Take(2).ToArray(); 
    return beginning.Length == 1 ? beginning[0] : default(T); 
} 
+0

正在转换为数组必要吗?基于[这个相关问题的答案](http://*.com/a/1993398/2137382)我想你可以检查'Take(2).Count()== 1',如果是的话,返回'source.Single()'。 –

+3

@JoeSewell - 转换为数组是必要的。如果你做'.Take(2).Count()',然后'.Single()'你迭代枚举两次,并且不能保证每次枚举总是产生相同的值。 – Enigmativity

+2

一些枚举可能甚至会在第二次失败。 –

你有没有试着写一个自定义的扩展方法是这样的:

public static TSource MyFirstOrDefault<TSource>(this IEnumerable<TSource> source) 
{ 
    return source.Count() == 1 ? source.First() : default(TSource); 
} 

,但你必须记住,方法Count()First()将需要实例元素的内集合,以获得计数(或第一个元素)。

希望,那会帮助你。

+1

这会引发异常。计数为零时没有第一项。除此之外,源有两个项目时返回什么值? –

+0

@RuardvanElburg - 谢谢你的提示(我修正了错字)。 – MaKCbIMKo

微软的落实Enumerable.FirstOrDefault直接与底层的枚举。您也可以:

public static T FirstIfOnlyOne<T>(this IEnumerable<T> source, T defaultValue = default(T)) 
{ 
    // (Null check and IList<T> optimization omitted...) 

    using (IEnumerator<T> enumerator = source.GetEnumerator()) 
    { 
     if (enumerator.MoveNext()) // Is there at least one item? 
     { 
      T item = enumerator.Current; // Save it. 
      if (!enumerator.MoveNext()) // Is that it? 
       return item; 
     } 
    } 
    return defaultValue; 
} 

这可能是最有效的实现方式。

+1

我认为这是最好的答案。 – Bigeyes