Відповіді:
Я знаю, що інші написали, чому ви використовуєте те чи інше, але я подумав, що я проілюструю, чому НЕ слід використовувати одне, коли ви маєте на увазі інше.
Примітка: У моєму коді, я зазвичай використовую FirstOrDefault()
і , SingleOrDefault()
але це інше питання.
Візьмемо, наприклад, таблицю, яка зберігає Customers
різними мовами за допомогою складеного ключа ( ID
, Lang
):
DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 ).First();
Цей код вище вводить можливу логічну помилку (важко простежити). Він поверне більше одного запису (якщо у вас є запис клієнта на кількох мовах), але він завжди поверне лише перший ... який може працювати іноді ..., але не інші. Це непередбачувано.
Оскільки ваш намір - повернути Одноразове Customer
користування Single()
;
Наступне може призвести до виключення (що саме ви хочете в цьому випадку):
DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 ).Single();
Тоді, ти просто б'єш себе в лоб і кажеш собі: OOPS! Я забув мовне поле! Далі правильна версія:
DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 && c.Lang == "en" ).Single();
First()
корисний у наступному сценарії:
DBContext db = new DBContext();
NewsItem newsitem = db.NewsItems.OrderByDescending( n => n.AddedDate ).First();
Він поверне ОДИН об’єкт, і оскільки ви використовуєте сортування, це буде останній запис, який повертається.
Використання, Single()
коли відчуваєш, що явно завжди повинен повертати 1 запис, допоможе уникнути логічних помилок.
customers.Where(predicate).Single()
customers.Single(predicate)
?
Single викине виняток, якщо знайде більше одного запису, що відповідає критеріям. Перший завжди буде вибирати перший запис зі списку. Якщо запит повертає лише 1 запис, ви можете перейти з ним First()
.
Обидва викинуть InvalidOperationException
виняток, якщо колекція порожня. Можна також використовувати SingleOrDefault()
. Це не призведе до виключення, якщо список порожній
Одномісний ()
Повертає окремий конкретний елемент запиту
При використанні : якщо очікується рівно 1 елемент; не 0 або більше 1. Якщо список порожній або містить більше одного елемента, він видасть виняток "Послідовність містить більше одного елемента"
SingleOrDefault ()
Повертає окремий конкретний елемент запиту або значення за замовчуванням, якщо результату не знайдено
При використанні : Коли очікується 0 або 1 елемент. Це буде винятком, якщо в списку є 2 і більше елементів.
Перший()
Повертає перший елемент запиту з кількома результатами.
При використанні : Коли очікується 1 або більше елементів, і ви хочете лише перший. Це буде винятком, якщо список не містить елементів.
FirstOrDefault ()
Повертає перший елемент списку з будь-якою кількістю елементів або значенням за замовчуванням, якщо список порожній.
При використанні : Коли очікується кілька елементів, і ви хочете лише перший. Або список порожній, і ви хочете значення за замовчуванням для вказаного типу, таке ж, як
default(MyObjectType)
. Наприклад: якщо тип списку,list<int>
він поверне перше число зі списку або 0, якщо список порожній. Якщо це такlist<string>
, він поверне перший рядок зі списку або null, якщо список порожній.
First
коли очікується 1 або більше елементів , не тільки "більше 1", а FirstOrDefault
також будь-яка кількість елементів.
Між цими двома методами існує тонка, смислова різниця.
Використовуйте Single
для отримання першого (і єдиного) елемента з послідовності, яка повинна містити один елемент і не більше. Якщо в послідовності більше елемента, ваш виклик Single
викликає викид виключення, оскільки ви вказали, що має бути лише один елемент.
Використовуйте First
для отримання першого елемента з послідовності, яка може містити будь-яку кількість елементів. Якщо в послідовності більше, ніж на елементі, ваш виклик First
не призведе до викидання винятку, оскільки ви вказали, що вам потрібен лише перший елемент у послідовності, і не хвилюється, чи існує більше.
Якщо послідовність не містить елементів, обидва виклики методу призведуть до викидів винятків, оскільки обидва методи очікують наявності принаймні одного елемента.
Якщо ви не хочете, щоб виняток було викинуто у випадку, якщо їх є більше, використовуйтеFirst()
.
Обидва ефективні, візьміть перший пункт. First()
трохи ефективніше, тому що не турбує перевірку, чи є другий предмет.
Єдина відмінність полягає в тому, що Single()
сподіваємось, що в перерахунку буде лише один елемент, і він викине виняток, якщо їх буде більше. Ви використовуєте, .Single()
якщо ви конкретно хочете, щоб у цьому випадку було виключено виняток .
Якщо я пам'ятаю, Single () перевіряє, чи є інший елемент після першого (і кидає виняток, якщо це так), тоді як First () зупиняється після його отримання. Обидва кидають виняток, якщо послідовність порожня.
Особисто я завжди використовую First ().
Щодо продуктивності: ми з колегою обговорювали ефективність Single / First (або SingleOrDefault vs FirstOrDefault), і я стверджував, що First (або FirstOrDefault) буде швидше і покращить продуктивність (я все про те, щоб зробити наш додаток бігайте швидше).
Я прочитав кілька публікацій про стек переповнення, які обговорюють це. Деякі кажуть, що невеликі підвищення продуктивності використовують First, а не Single. Це тому, що First просто поверне перший елемент, тоді як Single повинен сканувати всі результати, щоб переконатися, що немає дублікату (тобто: якщо він знайшов елемент у першому рядку таблиці, він все одно скануватиме кожен інший рядок у переконайтеся, що немає другого значення, яке відповідає умові, яке потім призведе до помилки). Я відчував, що перебуваю на твердій землі, коли "Перший" виявляється швидшим за "Одномісний", тому я надумав це довести і перестав дискусію в спокій.
Я встановив тест у своїй базі даних та додав 1 000 000 рядків ID UniqueIdentifier Foreign UniqueIdentifier Info nvarchar (50) (заповнений рядками цифр "0" до "999,9999"
Я завантажив дані та встановив ідентифікатор як поле первинного ключа.
Використовуючи LinqPad, моєю метою було показати, що якщо ви шукаєте значення на "Foreign" або "Info" за допомогою Single, це буде набагато гірше, ніж використання First.
Я не можу пояснити отримані результати. Майже у кожному випадку використання Single або SingleOrDefault було трохи швидше. Це не має для мене ніякого логічного сенсу, але я хотів поділитися цим.
Наприклад: я використав такі запити:
var q = TestTables.First(x=>x.Info == "314638") ;
//Vs.
Var q = TestTables.Single(x=>x.Info =="314638") ; //(this was slightly faster to my surprise)
Я спробував подібні запити в ключовому полі "Іноземне", яке не було індексованим мисленням, яке виявило б "Перше" швидше, але "Один" завжди був трохи швидшим у своїх тестах.
Вони різні. Обидва вони стверджують, що набір результатів не порожній, але одиничний також стверджує, що не більше 1 результату. Я особисто використовую Single в тих випадках, коли я очікую лише 1 результат, тому що повернення більш ніж одного результату є помилкою, і, ймовірно, слід ставитися до цього.
Ви можете спробувати простий приклад, щоб отримати різницю. Виняток буде кинутий у рядку 3;
List<int> records = new List<int>{1,1,3,4,5,6};
var record = records.First(x => x == 1);
record = records.Single(x => x == 1);
Записи в особі працівника:
Employeeid = 1
: Лише один працівник із цим посвідченням особи
Firstname = Robert
: Більше одного працівника з таким ім’ям
Employeeid = 10
: Жоден працівник із цим посвідченням особи
Тепер потрібно зрозуміти, що Single()
і що First()
означає докладно.
Одномісний ()
Single () використовується для повернення єдиного запису, який однозначно існує в таблиці, тому запит нижче поверне працівника, чий у employeed =1
нас є лише один Співробітник, чий Employeed
1. Якщо у нас є два записи, EmployeeId = 1
він видає помилку (див. помилка нижче в другому запиті, для якого ми використовуємо приклад Firstname
.
Employee.Single(e => e.Employeeid == 1)
Наведене поверне єдиний запис, у якому 1 employeeId
Employee.Single(e => e.Firstname == "Robert")
Вище викладене виняток, оскільки записи багатоядерності є в таблиці для FirstName='Robert'
. Виняток буде
InvalidOperationException: Послідовність містить більше одного елемента
Employee.Single(e => e.Employeeid == 10)
Це знову ж таки призведе до виключення, оскільки для id = 10 не існує запису. Виняток буде
InvalidOperationException: Послідовність не містить елементів.
Для EmployeeId = 10
поверне нуль, але , як ми використовуємо Single()
це буде згенеровано повідомлення про помилку. Для обробки нульової помилки нам слід скористатися SingleOrDefault()
.
Перший()
Перший () повертає з декількох записів відповідні записи, відсортовані у порядку зростання відповідно до birthdate
того, він поверне «Роберт», який є найстарішим.
Employee.OrderBy(e => e. Birthdate)
.First(e => e.Firstname == "Robert")
Зверху слід повернути найстаріший, Роберт, згідно з DOB.
Employee.OrderBy(e => e. Birthdate)
.First(e => e.Employeeid == 10)
Вище буде викинуто виняток, оскільки не існує запису для id = 10. Щоб уникнути нульового винятку, ми повинні використовувати, FirstOrDefault()
а не First()
.
Примітка. Ми можемо використовувати лише First()
/ Single()
коли ми абсолютно впевнені, що він не може повернути нульове значення.
В обох функціях використовуйте SingleOrDefault () OR FirstOrDefault (), який буде обробляти нульовий виняток, у випадку відсутності запису він поверне null.