LINQシングルvsファースト


214

LINQ:

クエリが単一のレコードを返すことが確実であることがわかっSingle()ているFirst()場合は、演算子を使用する方が効率的ですか?

違いはありますか?

回答:


311

単一のレコードが必要な場合は、コードで明示することを常にお勧めします。

他の人があなたがどちらか一方を使用する理由を書いたことを知っていますが、もう一方を意味するとき、なぜあなたがどちらかを使用すべきでないかを説明したいと思いました。

注:私のコードでは通常使用FirstOrDefault()しますSingleOrDefault()が、それは別の質問です。

たとえば、Customers複合キー(IDLang)を使用してさまざまな言語で格納するテーブルを考えます。

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();

次に、額に自分をぶつけて、自分に言います。おっと!言語フィールドを忘れてしまった!以下は正しいバージョンです。

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();

これは1つのオブジェクトを返し、並べ替えを使用しているため、返されるのは最新のレコードになります。

Single()常に明示的に常に1レコードを返す必要があると思われる場合に使用すると、論理エラーを回避するのに役立ちます。


76
SingleメソッドとFirstメソッドはどちらもフィルタリング用の式パラメーターを取ることができるため、Where関数は必要ありません。例:顧客customer = db.Customers.Single(c => c.ID == 5);
Josh Noe 2013

6
@JoshNoe-私は好奇心旺盛ですが、違いはありcustomers.Where(predicate).Single() customers.Single(predicate)ますか?
drzaus 2013年

9
@drzaus-論理的に、いいえ、どちらも述語に基づいて返される値をフィルタリングします。しかし、私は逆アセンブリをチェックアウトし、Where(述語).Single()には、私が作成した単純なケースで3つの追加の命令があります。したがって、私はILの専門家ではありませんが、customers.Single(述語)の方が効率的であるようです。
Josh Noe

5
@JoshNoe結局のところ、それは正反対です。
AgentFire、2014年

10
@AgentFireステートメントをバックアップするには
M. Mimpen 2014年

72

Singleは、基準に一致するレコードが複数見つかった場合に例外をスローします。最初は常にリストから最初のレコードを選択します。クエリが1つのレコードのみを返す場合は、を使用できますFirst()

InvalidOperationExceptionコレクションが空の場合、どちらも例外をスローします。または、を使用できますSingleOrDefault()。リストが空の場合、これは例外をスローしません


29

シングル()

クエリの単一の特定の要素を返します

使用時:要素が1つだけ必要な場合。0または1以下。リストが空の場合、または複数の要素がある場合は、「シーケンスに複数の要素が含まれています」という例外がスローされます。

SingleOrDefault()

クエリの単一の特定の要素、または結果が見つからない場合はデフォルト値を返します

使用時:0または1要素が必要な場合。リストに2つ以上のアイテムがある場合、例外がスローされます。

最初()

複数の結果を持つクエリの最初の要素を返します。

使用時:1つ以上の要素が期待され、最初の要素のみが必要な場合。リストに要素が含まれていない場合は、例外がスローされます。

FirstOrDefault()

任意の量の要素を含むリストの最初の要素、またはリストが空の場合はデフォルト値を返します。

使用時:複数の要素が予想され、最初の要素のみが必要な場合。または、リストが空で、指定したタイプのデフォルト値をと同じにする必要がありますdefault(MyObjectType)。例:リストのタイプがリストlist<int>の最初の数値を返す場合、またはリストが空の場合は0を返します。の場合list<string>、リストの最初の文字列を返します。リストが空の場合はnullを返します。


1
いい説明。「複数」だけでなく、要素がいくつでもあるFirst場合に、1つ以上の要素が期待される場合にのみ使用できるように変更しますFirstOrDefault
Andrew

18

これらの2つの方法には微妙な意味上の違いがあります。

Single1つ以上の要素を含まないシーケンスから最初の(そして唯一の)要素を取得するために使用します。シーケンスに複数の要素Singleがある場合は、要素が1つだけであることを示しているため、を呼び出すと例外がスローされます。

First任意の数の要素を含むことができるシーケンスから最初の要素を取得するために使用します。シーケンスに複数の要素がある場合、シーケンスFirstの最初の要素のみが必要であり、それ以上存在してもかまわないことを示しているため、呼び出しによって例外がスローされることはありません。

シーケンスに要素が含まれていない場合、両方のメソッドが少なくとも1つの要素の存在を期待しているため、両方のメソッド呼び出しで例外がスローされます。


17

複数のアイテムがある場合に例外がスローされるようにしたくない場合は、を使用しますFirst()

どちらも効率的です。最初の項目を取ってください。 First()2番目のアイテムがあるかどうかを確認する必要がないので、少し効率的です。

唯一の違いはSingle()、列挙には最初から1つの項目しかないと想定されているため、複数ある場合は例外がスローされます。あなたは使い .Single() ますが、具体的に例外がスローされたい場合は、このケースでは。


14

私が思い出すと、Single()は最初の要素の後に別の要素があるかどうかをチェックし(そうである場合は例外をスローします)、First()はそれを取得した後に停止します。シーケンスが空の場合、どちらも例外をスローします。

個人的には、常にFirst()を使用します。


2
SQLでは、私が間違っていなければ、First()はTOP 1を実行し、Single()はTOP 2を実行します。
Matthijs Wessels 2011年

10

パフォーマンスについて:同僚と私はSingle vs First(またはSingleOrDefault vs FirstOrDefault)のパフォーマンスについて話し合っており、First(またはFirstOrDefault)の方が高速でパフォーマンスが向上するという点を主張していました(私はすべてアプリを作成することです)より速く実行する)。

これについて議論するStack Overflowの投稿をいくつか読んだことがあります。SingleではなくFirstを使用するとパフォーマンスが少し向上すると言う人もいます。これは、Firstが単に最初のアイテムを返す一方で、Singleがすべての結果をスキャンして重複がないことを確認する必要があるためです(つまり、テーブルの最初の行にアイテムが見つかった場合でも、1行おきにスキャンしてエラーをスローする条件に一致する2番目の値がないことを確認してください)。「ファースト」の方が「シングル」よりも速いという確固たる地位にいるように感じたので、それを証明し、議論を休止しました。

データベースにテストをセットアップし、1,000,000行のID UniqueIdentifier Foreign UniqueIdentifier Info nvarchar(50)を追加しました(「0」から「999,9999」までの数字の文字列が入力されています)

データを読み込み、IDを主キーフィールドとして設定しました。

LinqPadを使用して、私の目標は、Singleを使用して「Foreign」または「Info」の値を検索した場合、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)

Firstの方が速いことを証明するインデックス付きの考えではない 'Foreign'キーフィールドで同様のクエリを試してみましたが、私のテストではSingleは常にわずかに高速でした。


2
インデックス付きフィールド上にある場合、データベースが一意であることを確認するためにスキャンを実行する必要はありません。それはすでに知っています。したがって、オーバーヘッドはありません。サーバー側の唯一のオーバーヘッドは、1つのレコードのみが返されることを保証することです。パフォーマンステストを自分で行うことは、どちらにしても決定的ではありません
mirhagk

1
IComparerで使用されていないフィールドを検索した場合、これらの結果は複雑なオブジェクトでは同じになるとは思いません。
アンソニーニコルズ

それは別の質問になるはずです。テストのソースを必ず含めてください。
Sinatr 2017年

5

彼らは違う。どちらも結果セットが空ではないと断言しますが、singleは結果が1つしかないと断言します。1つ以上の結果を取得することはエラーであり、おそらくそのように扱われるべきであるという理由だけで、結果が1つだけであると期待する場合に個人的にSingleを使用します。


5

あなたは違いを得るために簡単な例を試すことができます。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);

3

私が知っている多くの人がFirstOrDefault()を使用していますが、SingleOrDefault()を使用する傾向があります。1つ以上あると、データの不整合が発生することが多いからです。ただし、これはLINQ-to-Objectsを扱っています。


-1

従業員エンティティのレコード:

Employeeid = 1:このIDを持つ従業員は1人のみ

Firstname = Robert:この名前の複数の従業員

Employeeid = 10:このIDの従業員はいません

今では何を理解することが必要だSingle()First()詳細に意味します。

シングル()

Single()は、テーブルに一意に存在する単一のレコードを返すために使用されます。したがって、以下のクエリemployeed =1は、1であるEmployeeが1つしかないため、Employeeを返しますEmployeed。2つのレコードがある場合EmployeeId = 1、エラーがスローされます(の例を使用している2番目のクエリの以下のエラー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は、nullを返しますが、私たちが使用しているとしてSingle()、それはエラーがスローされます。nullエラーを処理するには、を使用する必要がありますSingleOrDefault()

最初()

First()は、複数のレコードから対応するレコードを昇順で並べ替えてbirthdate返すため、最も古い 'Robert'が返されます。

Employee.OrderBy(e => e. Birthdate)
.First(e => e.Firstname == "Robert")

上記は、DOBに従って、最も古いもの、Robertを返す必要があります。

Employee.OrderBy(e => e. Birthdate)
.First(e => e.Employeeid == 10)

上記では、id = 10のレコードが存在しないため、例外がスローされます。null例外を回避するには、FirstOrDefault()ではなくを使用する必要がありますFirst()

注:null値を返せないことが確実である場合にのみ、First()/ を使用Single()できます。

どちらの関数でも、SingleOrDefault()またはFirstOrDefault()を使用してnull例外を処理します。レコードが見つからない場合はnullを返します。


答えを説明してください。
マービン氏

1
@MrMaavin更新しました。理解できるようになりましたか?
保安官
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.