2017-02-19 39 views
2

Мне хотелось бы что-то вроде IEnumerable.FirstOrDefault, но я хотел бы вернуть значение по умолчанию, если length != 1. Что-то вроде v.Count() == 1 ? v[0] : default. Но я бы предпочел сделать это, не превратив все перечислимое в массив. Каков наилучший способ получить это поведение?Как я могу получить первое значение IEnumerable, если длина равна точно одному, или по умолчанию в противном случае?

ответ

7

Это хорошая идея, чтобы избежать повторения перечислимого более одного раза, поскольку не все перечисляемые повторяют одни и те же значения.

Чтобы гарантировать, что ваш перечислимый возвращает только 1 элемент, наиболее эффективно перенести первые два элемента из перечислимого и преобразовать результат в массив. Если массив имеет два элемента, вы знаете, что перечисляемый имеет более одного элемента без повторения всего перечислимого. Можно перечислить бесконечно долго.

Так вот как это сделать:

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://stackoverflow.com/a/1993398/2137382) Я думаю, вы можете просто проверить, есть ли «Take (2) .Count() == 1', и если да , return 'source.Single()'. –

+3

@JoeSewell - требуется преобразование в массив. Если вы делаете '.Take (2) .Count()', а затем '.Single()' вы повторяете перечислимое дважды, и нет никакой гарантии, что перечислимое всегда производит одинаковые значения каждый раз. – Enigmativity

+2

Некоторые перечислимые объекты могут даже не работать во второй раз. –

0

Вы пробовали написать метод пользовательского расширения, что-то вроде:

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

но вы должны иметь в виду, что методы Count() и First() необходимо для конкретизации внутреннюю коллекцию элементов для того, чтобы получить count (или первый элемент).

Надеюсь, это вам поможет.

+1

Это вызовет исключение. Нет первого элемента, если счетчик равен нулю. Кроме того, какое значение возвращается, когда источник имеет два элемента? –

+0

@RuardvanElburg - спасибо за примечание (я исправил опечатку). – MaKCbIMKo

9

реализация Microsoft по 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

 Смежные вопросы

  • Нет связанных вопросов^_^