Мне хотелось бы что-то вроде IEnumerable.FirstOrDefault
, но я хотел бы вернуть значение по умолчанию, если length != 1
. Что-то вроде v.Count() == 1 ? v[0] : default
. Но я бы предпочел сделать это, не превратив все перечислимое в массив. Каков наилучший способ получить это поведение?Как я могу получить первое значение IEnumerable, если длина равна точно одному, или по умолчанию в противном случае?
ответ
Это хорошая идея, чтобы избежать повторения перечислимого более одного раза, поскольку не все перечисляемые повторяют одни и те же значения.
Чтобы гарантировать, что ваш перечислимый возвращает только 1 элемент, наиболее эффективно перенести первые два элемента из перечислимого и преобразовать результат в массив. Если массив имеет два элемента, вы знаете, что перечисляемый имеет более одного элемента без повторения всего перечислимого. Можно перечислить бесконечно долго.
Так вот как это сделать:
public static T OneOnlyThenFirstOrDefault<T>(this IEnumerable<T> source)
{
var beginning = source.Take(2).ToArray();
return beginning.Length == 1 ? beginning[0] : default(T);
}
Вы пробовали написать метод пользовательского расширения, что-то вроде:
public static TSource MyFirstOrDefault<TSource>(this IEnumerable<TSource> source)
{
return source.Count() == 1 ? source.First() : default(TSource);
}
но вы должны иметь в виду, что методы Count()
и First()
необходимо для конкретизации внутреннюю коллекцию элементов для того, чтобы получить count (или первый элемент).
Надеюсь, это вам поможет.
Это вызовет исключение. Нет первого элемента, если счетчик равен нулю. Кроме того, какое значение возвращается, когда источник имеет два элемента? –
@RuardvanElburg - спасибо за примечание (я исправил опечатку). – MaKCbIMKo
реализация 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;
}
Возможно, это наиболее эффективная реализация.
Я думаю, что это лучший ответ. – Bigeyes
Преобразование в массив необходимо? На основании [этого ответа на соответствующий вопрос] (http://stackoverflow.com/a/1993398/2137382) Я думаю, вы можете просто проверить, есть ли «Take (2) .Count() == 1', и если да , return 'source.Single()'. –
@JoeSewell - требуется преобразование в массив. Если вы делаете '.Take (2) .Count()', а затем '.Single()' вы повторяете перечислимое дважды, и нет никакой гарантии, что перечислимое всегда производит одинаковые значения каждый раз. – Enigmativity
Некоторые перечислимые объекты могут даже не работать во второй раз. –