Ответ 1
Используйте сначала, когда вы знаете, что в коллекции есть один или несколько элементов. Используйте Single, когда вы знаете, что в коллекции есть только один элемент. Если вы этого не знаете, не используйте эти методы. Используйте методы, которые делают что-то еще, например FirstOrDefault(), SingleOrDefault() и т.д.
Вы могли бы, например, сказать:
int? first = sequence.Any() ? (int?) sequence.First() : (int?) null;
что намного меньше, чем
int? first = null;
try { first = sequence.First(); } catch { }
Но все еще не очень хорошо, потому что он повторяет первый элемент последовательности дважды. В этом случае я бы сказал, если нет операторов последовательности, которые делают то, что вам нужно, тогда напишите свой собственный.
Продолжая наш пример, предположим, что у вас есть последовательность целых чисел и вы хотите получить первый элемент, или, если таковой нет, верните null. Для этого не существует встроенного оператора последовательности, но его легко написать:
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(), который использует предикат как удобство, так что вам не нужно вводить дополнительные "Where".
ОБНОВЛЕНИЕ: Дэйв задает еще один следующий вопрос, который, я думаю, может быть "что, если я хочу сказать последовательность .FirstOrNull(). Frob().Bhah(). Что бы ни было(), но любой из них по линии return null?"
Мы рассмотрели вопрос о добавлении к С# оператора распространения с нулевым распространением, предварительно обозначенного как ?.
, то есть вы могли бы сказать
x = a?.b?.c?.d;
и если a, b или c произведут нуль, то результатом будет присвоение значения null переменной x.
Очевидно, мы фактически не реализовали его для С# 4.0. Это возможный рабочий элемент для гипотетических будущих версий языка... ОБНОВЛЕНИЕ "Оператор Elvis" добавлен в С# 6.0, yay!
Обратите внимание, что С# имеет нулевой коалесцирующий оператор:
(sequence.FirstOrNull()?? GetDefault()). Frob(). Blah(). Whatever()
означает "Если FirstOrNull возвращает non-null, используйте его как приемник 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(), если этого не происходит.