何を見たいですか?
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");
ここでのベストプラクティスは何ですか?どちらが例外をよりわかりやすくしますか?
SingleOrDefault()
を使用しますSingle()
を使用し、0または2以上ではなく、アイテムが必要また、考えられるシナリオがいくつかあることに注意してください。
そして:
First()
、FirstOrDefault()
、Any()
についても忘れないでください
私は書きます:
var item = list.Single(x => x.HasFoo);
これが単一のアイテムを返さないケースが非常に一般的で、わかりやすいエラーメッセージが必要な場合、それは本当に例外ですか?
実際には、同じです。しかし、私は2番目を優先 1なので、最初の2つの例外が1つスローされます。 例外は高額です。
書いてもいいと思います
var item = list.SingleOrDefault(x => x.HasFoo);
if (item == null) ...
しかし、あなたも書くことができます
if (list.Any(x => x.HasFoo)) ...
実際に値にアクセスする必要がない場合。
リストの要素を常に期待している場合は、
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シナリオについて質問していたとすると、私は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");
}
私はキーレン・ジョンストーンに同意します。例外が発生するのを待たないでください。これはかなりコストがかかります。このメソッドを何度も呼び出す場合は必ず確認してください。
最初のコードスニペットは、元の例外を待って、新しい例外をスローするよりもさらに高価です。
シングル
要素の一致が見つかった場合は、要素のコレクションから単一の特定の要素を返します。コレクション内でその要素に一致するものが見つからなかった場合、または複数見つかった場合は、例外がスローされます。
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();
}
}
}