Используйте Сначала, когда вы знаете , что в коллекции есть один или несколько предметов. Используйте Single, если вы знаете , что в коллекции ровно один элемент. Если вы не знаете этих вещей, то не используйте эти методы . Используйте методы, которые делают что-то еще, например FirstOrDefault (), SingleOrDefault () и т. Д.
Вы могли бы, например, сказать:
int? first = sequence.Any() ? (int?) sequence.First() : (int?) null;
, который намного менее груб, чем
int? first = null;
try { first = sequence.First(); } catch { }
Но все же не очень хорошо, потому что он повторяет первый элемент последовательности дважды. В этом случае я бы сказал, что если нет операторов последовательности, которые делают то, что вы хотите, то напишите свой собственный .
Продолжая наш пример, предположим, что у вас есть последовательность целых чисел, и вы хотите получить первый элемент, или, если ее нет, вернуть ноль. Нет встроенного оператора последовательности, который делает это, но его легко написать:
public static int? FirstOrNull(this IEnumerable<int> sequence)
{
foreach(int item in sequence)
return item;
return null;
}
или даже лучше:
public static T? FirstOrNull<T>(this IEnumerable<T> sequence) where T : struct
{
foreach(T item in sequence)
return item;
return null;
}
или это:
struct Maybe<T>
{
public T Item { get; private set; }
public bool Valid { get; private set; }
public Maybe(T item) : this()
{ this.Item = item; this.Valid = true; }
}
public static Maybe<T> MyFirst<T>(this IEnumerable<T> sequence)
{
foreach(T item in sequence)
return new Maybe(item);
return default(Maybe<T>);
}
...
var first = sequence.MyFirst();
if (first.Valid) Console.WriteLine(first.Item);
Но что бы вы ни делали, не обрабатывайте те исключения , которые вы упомянули. Эти исключения не предназначены для обработки, они должны сообщить вам, что в вашем коде есть ошибки . Вы не должны обращаться с ними, вы должны исправлять ошибки. Помещение ловушек вокруг них - это сокрытие ошибок , а не исправление ошибок .
UPDATE:
Дейв спрашивает, как создать FirstOrNull, который принимает предикат. Достаточно просто. Вы можете сделать это так:
public static T? FirstOrNull<T>(this IEnumerable<T> sequence, Func<T, bool> predicate) where T : struct
{
foreach(T item in sequence)
if (predicate(item)) return item;
return null;
}
Или вот так
public static T? FirstOrNull<T>(this IEnumerable<T> sequence, Func<T, bool> predicate) where T : struct
{
foreach(T item in sequence.Where(predicate))
return item;
return null;
}
Или даже не беспокойтесь:
var first = sequence.Where(x=>whatever).FirstOrNull();
Нет причин, по которым предикат должен идти в FirstOrNull. Мы предоставляем First (), который принимает предикат для удобства, чтобы вам не приходилось вводить лишнее «Где».
ОБНОВЛЕНИЕ: Дейв задает еще один дополнительный вопрос, который, я думаю, может быть «что, если я хочу сказать sequence.FirstOrNull (). Frob (). Blah (). Независимо от (), но любой из тех, кто находится на этой линии, может вернуть ноль? "
Мы рассмотрели вопрос о добавлении в C # оператора доступа к элементу с нулевым распространением, условно обозначенного как ?.
- то есть можно сказать
x = a? .B? .C? .D;
и если a, b или c выдают ноль, то результатом будет присвоение нолю x.
Очевидно, что мы на самом деле не реализовали это для C # 4.0. Это возможный рабочий элемент для гипотетических будущих версий языка ... ОБНОВЛЕНИЕ "Оператор Элвиса" был добавлен в C # 6.0, ууу!
Обратите внимание, что в C # есть есть оператор объединения нулей:
(sequence.FirstOrNull () ?? GetDefault ()). Frob (). Blah (). Независимо от ()
означает «Если FirstOrNull возвращает ненулевое значение, используйте его в качестве получателя Frob, в противном случае вызовите GetDefault и используйте его в качестве получателя». Альтернативным подходом было бы снова написать свой собственный:
public static T FirstOrLazy<T>(this IEnumerable<T> sequence, Func<T> lazy)
{
foreach(T item in sequence)
return item;
return lazy();
}
sequence.FirstOrLazy(()=>GetDefault()).Frob().Blah().Whatever();
Теперь вы получаете первый элемент, если он есть, или результат вызова GetDefault (), если его нет.