В чем различия между FirstOrDefault и SingleOrDefault в LINQ?

Ответ

FirstOrDefault и SingleOrDefault — методы расширения LINQ с разной семантикой гарантий относительно количества элементов в результирующей последовательности.

Критерий FirstOrDefault SingleOrDefault
Ожидаемое количество 0 или более (берётся первый) Строго 0 или 1
Поведение при 0 элементов Возвращает default(T) (например, null) Возвращает default(T)
Поведение при 2+ элементах Возвращает первый элемент Выбрасывает InvalidOperationException
Производительность Завершается при нахождении первого элемента Всегда проверяет всю последовательность на наличие второго элемента

Примеры:

// Коллекция с дубликатами
var numbers = new List<int> { 1, 2, 3, 2, 5 };

var firstEven = numbers.FirstOrDefault(x => x % 2 == 0); // 2 (первое чётное)
// Это сработает, хотя чётных чисел несколько.

var singleEven = numbers.SingleOrDefault(x => x % 2 == 0); // InvalidOperationException!
// Исключение, потому что чётных чисел больше одного.

// Корректное использование SingleOrDefault — когда элемент должен быть уникальным
var singleOne = numbers.SingleOrDefault(x => x == 1); // 1 (единственный)
var singleTen = numbers.SingleOrDefault(x => x == 10); // 0 (не найдено)

Когда что использовать:

  • FirstOrDefault — когда вам нужен любой подходящий элемент (например, «найти активного пользователя» — их может быть несколько, но берём первого).
  • SingleOrDefault — когда вы ожидаете строго один или ноль элементов, и наличие дубликатов является ошибкой данных (например, «найти пользователя по уникальному email»).

Совет: Использование SingleOrDefault — это явное утверждение об уникальности. Если есть сомнения, используйте FirstOrDefault.

Ответ 18+ 🔞

А, ну вот опять эти два метода, которые всех путают, как близнецы-хулиганы в школе. Смотри, тут вся соль в том, что они по-разному относятся к количеству элементов в выборке, и если перепутать — получишь исключение в лицо, как хуем по лбу.

FirstOrDefault — это такой спокойный чувак: «Ну есть элементы — ок, дай первого, нет — ну и похуй, верну дефолт». Ему вообще по барабану, сколько там их в коллекции — один, сто, хоть миллион. Нашёл первого подходящего — сразу отдал и пошёл пиво пить.

А вот SingleOrDefault — это уже параноик с проверкой на уникальность. Он такой: «Слушай, если элемент есть — он должен быть ОДИН, блядь. Если их нет — ну ладно. Если их два или больше — это пиздец, нарушение контракта, я на тебя исключение кину!». И он реально пройдётся по всей коллекции до конца, чтобы убедиться, что второго такого же нет.

Вот смотри на примере, чтобы совсем понятно стало:

var numbers = new List<int> { 1, 2, 3, 2, 5 };

// FirstOrDefault — берёт первое чётное и не парится
var firstEven = numbers.FirstOrDefault(x => x % 2 == 0); // вернёт 2 и доволен

// SingleOrDefault — тут же начнёт орать, потому что чётных чисел два
var singleEven = numbers.SingleOrDefault(x => x % 2 == 0); // InvalidOperationException, ёпта!

Так когда что юзать, чтобы не обосраться?

  • FirstOrDefault — когда тебе просто нужен какой-нибудь подходящий элемент. Типа «найди мне любого активного пользователя». Их может быть хоть двадцать, но тебе лишь бы первого взять.
  • SingleOrDefault — это когда ты на полном серьёзе ожидаешь либо ноль, либо ОДИН элемент. Например, «найди пользователя по его уникальному email». Если вдруг окажется два пользователя с одним email — это уже пиздец в данных, и тебе лучше узнать об этом сразу через исключение, чем потом хуй пойми где багу ловить.

Короче, SingleOrDefault — это как красная кнопка, нажимай только если уверен в уникальности. Во всех остальных случаях — FirstOrDefault, и живи спокойно.