web-dev-qa-db-ja.com

要素が見つからない可能性がある場合は、Single()またはSingleOrDefault()を使用する必要がありますか?

何を見たいですか?

try
{
  var item = list.Single(x => x.HasFoo);
}
catch(InvalidOperationException e)
{
  throw new InvalidOperationException("Exactly one item with foo expected, none found", e);
}

または:

var item = list.SingleOrDefault(x => x.HasFoo);
if (item == null)
      throw new InvalidOperationException("Exactly one item with foo expected, none found");

ここでのベストプラクティスは何ですか?どちらが例外をよりわかりやすくしますか?

36
the_drow
  • 期待される項目が0または1の場合はSingleOrDefault()を使用します
  • 1の場合はSingle()を使用し、0または2以上ではなく、アイテムが必要

また、考えられるシナリオがいくつかあることに注意してください。

  • 0または1が期待されていたときに0を取得しました(ok)
  • 0または1が期待されていたときに1を取得しました(ok)
  • 0または1が予期されていたときに2以上を得た(エラー)

そして:

  • 1が予期されていたときに0を取得しました(エラー)
  • 1が期待されていたときに1を取得しました(ok)
  • 1が予期されていたときに2以上を得た(エラー)

First()FirstOrDefault()Any()についても忘れないでください

79
abatishchev

私は書きます:

var item = list.Single(x => x.HasFoo);

これが単一のアイテムを返さないケースが非常に一般的で、わかりやすいエラーメッセージが必要な場合、それは本当に例外ですか?

5
Myster

実際には、同じです。しかし、私は2番目を優先 1なので、最初の2つの例外が1つスローされます例外は高額です。

4
Aliostad

書いてもいいと思います

var item = list.SingleOrDefault(x => x.HasFoo);
if (item == null) ...

しかし、あなたも書くことができます

if (list.Any(x => x.HasFoo)) ...

実際に値にアクセスする必要がない場合。

3
Roy Dictus

リストの要素を常に期待している場合は、

var item = list.Single(x => x.HasFoo);

例外をキャッチし、例外の詳細をログに記録して、ユーザーにわかりやすいメッセージを表示します。

0要素または1要素以上を期待する場合、最も安全な方法は

var item = list.FirstOrDefault(x => x.HasFoo);
if (item == null) 
{ 
// empty list processing, not necessary throwing exception
}

私は、確認することは重要ではない、複数のレコードが存在するかどうかを仮定しました。

同様の質問がコードプロジェクトの記事 LINQ:Single vs. SingleOrDefault で議論されました

例外を待ってから新しい要素をスローするのではなく、要素を取得する前にリスト内の要素数のチェックを確認したいと思います。

var listFiltered = list.Where(x => x.HasFoo).ToList();
int listSize = listFiltered.Count();
if (listSize == 0)
{
    throw new InvalidOperationException("Exactly one item with foo expected, none found");
}
else if (listSize > 1)
{
    throw new InvalidOperationException("Exactly one item with foo expected, more than one found");
}

提案がコンパクトであることはいいですが、より明示的なIMOである方が良いです。

(また、あなたの提案では、例外は厳密には有効ではありません:複数存在する可能性がある場合、「見つかりません」と表示されます)

編集:Jeebus、1人の行を追加して、最初は知識豊富な人々のためにリストをフィルタリングしました。 (それは誰にとっても明白であると思いました)

1

..1シナリオについて質問していたとすると、私はSingleOrDefaultを選択します。これにより、「何も見つからない」シナリオを処理するための独自の方法を指定できるようになります。

だから、少し構文糖を使用する良い方法は次のようになります:

// assuming list is List<Bar>();
var item = list.SingleOrDefault(x => x.HasFoo) ?? notFound<Bar>();

notFound()は次のとおりです。

T notFound<T>()
{
  throw new InvalidOperationException("Exactly one item with foo expected, none found");
}
1
Juanjo

私はキーレン・ジョンストーンに同意します。例外が発生するのを待たないでください。これはかなりコストがかかります。このメソッドを何度も呼び出す場合は必ず確認してください。

最初のコードスニペットは、元の例外を待って、新しい例外をスローするよりもさらに高価です。

0
ChristiaanV

シングル

要素の一致が見つかった場合は、要素のコレクションから単一の特定の要素を返します。コレクション内でその要素に一致するものが見つからなかった場合、または複数見つかった場合は、例外がスローされます。

SingleOrDefault

要素の一致が見つかった場合は、要素のコレクションから単一の特定の要素を返します。コレクション内のその要素に対して複数の一致が見つかった場合、例外がスローされます。コレクション内のその要素に一致するものが見つからない場合、デフォルト値が返されます。

ここにサンプルの例があります:-

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LinqSingleorSingleOrDefault
{
class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string City { get; set; }
}

public class Program
{

    static void Main(string[] args)
    {
        IList<Employee> employeeList = new List<Employee>(){
            new Employee() { Id = 10, Name = "Chris", City = "London" },
            new Employee() { Id=11, Name="Robert", City="London"},
            new Employee() { Id=12, Name="Mahesh", City="India"},
            new Employee() { Id=13, Name="Peter", City="US"},
            new Employee() { Id=14, Name="Chris", City="US"}
        };

        //Single Example

        var result1 = employeeList.Single(); 
        // this will throw an InvalidOperationException exception because more than 1 element in employeeList.

        var result2 = employeeList.Single(e => e.Id == 11);
        //exactly one element exists for Id=11

        var result3 = employeeList.Single(e => e.Name == "Chris");
        // throws an InvalidOperationException exception because of more than 1 element contain for Name=Chris

        IList<int> intList = new List<int> { 2 };
        var result4 = intList.Single(); 
        // return 2 as output because exactly 1 element exists


        //SingleOrDefault Example

        var result5 = employeeList.SingleOrDefault(e => e.Name == "Mohan");
        //return default null because not element found for specific condition.

        var result6 = employeeList.SingleOrDefault(e => e.Name == "Chris");
        // throws an exception that Sequence contains more than one matching element

        var result7 = employeeList.SingleOrDefault(e => e.Id == 12);
        //return only 1 element

        Console.ReadLine();

    }
 }   
}
0
Nimesh khatri