以下のコード例があります。SelectMany()
をよりよく使用することで、これをよりクリーンにする方法に興味があります。この時点で、QuestionList
プロパティはnullになりません。必要なのは、nullでないanswerRows
のリストだけですが、Questions
もnullになることがあります。
IEnumerable<IQuestion> questions = survey.QuestionList
.Where(q => q.Questions != null)
.SelectMany(q => q.Questions);
if(questions == null)
return null;
IEnumerable<IAnswerRow> answerRows = questions
.Where(q => q.AnswerRows != null)
.SelectMany(q => q.AnswerRows);
if(answerRows == null)
return null;
UPDATE:var
を使用してもサンプルが十分に明確ではなかったため、コードを少し変更しました
質問は、LINQの使用についてもっと学ぶのを助けるためのものでした。
更新2:
JonのEnumerable.SelectMany
とNullに関するコメントに興味があったので、エラーの場所を簡単に確認できるように、偽のデータを使用して例を試してみました。以下を参照してください。具体的には、SelectMany()
SelectMany()
の結果については、問題がnull参照でSelectMany()
を使用しないようにする必要があることがわかりました。実際にNullReferenceException
name :(そして最後に物事をまとめます。
また、これを行っているときに、この例でのtry { } catch() { }
の使用は役に立たないことに気付きました。通常どおり、ジョンスキートには answer :)遅延実行があります。
行2の例外を表示する場合は、関連する行1のビット:Pをコメント化してください。コード例を書き直さずにこのエラーを停止する方法を理解できなかったのは残念です。
using System;
using System.Collections.Generic;
using System.Linq;
namespace SelectManyExample
{
class Program
{
static void Main(string[] args)
{
var questionGroupList1 = new List<QuestionGroup>() {
new QuestionGroup() {
Questions = new List<Question>() {
new Question() {
AnswerRows = new List<AnswerRow>() {
new AnswerRow(),
new AnswerRow()
}
},
// empty question, causes cascading SelectMany to throw a NullReferenceException
null,
new Question() {
AnswerRows = new List<AnswerRow>() {
new AnswerRow() {
Answers = new List<Answer>() {
new Answer(),
new Answer()
}
}
}
}
}
}
};
var questionGroupList2 = new List<QuestionGroup>() {
null,
new QuestionGroup()
};
IEnumerable<AnswerRow> answerRows1 = null;
IEnumerable<AnswerRow> answerRows2 = null;
try
{
answerRows1 = questionGroupList1
.SelectMany(q => q.Questions)
.SelectMany(q => q.AnswerRows);
}
catch(Exception e) {
Console.WriteLine("row 1 error = " + e.Message);
}
try
{
answerRows2 = questionGroupList2
.SelectMany(q => q.Questions)
.SelectMany(q => q.AnswerRows);
}
catch (Exception e)
{
Console.WriteLine("row 2 error = " + e.Message);
}
Console.WriteLine("row 1: " + answerRows1.Count());
Console.WriteLine("row 2: " + answerRows2.Count());
Console.ReadLine();
}
}
public class QuestionGroup {
public IEnumerable<Question> Questions { get; set; }
}
public class Question {
public IEnumerable<AnswerRow> AnswerRows { get; set; }
}
public class AnswerRow {
public IEnumerable<Answer> Answers { get; set; }
}
public class Answer {
public string Name { get; set; }
}
}
_survey.QuestionList
.Where(l => l.Questions != null)
.SelectMany(l => l.Questions)
.Where(q => q != null && q.AnswerRows != null)
.SelectMany(q => q.AnswerRows);
_
コレクションがnull
にならないようにすることをお勧めします。 null
をうまく処理しないと、少し面倒なことがあります。あなたは、コード全体でif (something != null) {}
に終わります。次に使用します:
_survey.QuestionList
.SelectMany(l => l.Questions)
.SelectMany(q => q.AnswerRows);
_
public static IEnumerable<TResult> SelectNotNull<TSource, TResult>(
this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector)
where TResult : class
{
return source.Select(selector)
.Where(sequence => sequence != null)
.SelectMany(x => x)
.Where(item => item != null);
}
これにより、次のことが可能になります。
var allAnswers = survey.QuestionList
.SelectNotNull(list => list.Questions)
.SelectNotNull(question => question.AnswerRows);
DRYに準拠するソリューションは、SelectMany
ラムダ式で null融合演算子_??
_ を使用することです。
_IEnumerable<IQuestion> questions = survey.QuestionList.SelectMany(q => q.Questions ?? Enumerable.Empty<IQuestion>());
IEnumerable<IAnswerRow> answerRows = questions.SelectMany(q => q.AnswerRows ?? Enumerable.Empty<IAnswerRow>());
_
OPのコードと上記のコードの両方で、questions
とanswerRows
がnullになることはないため、nullチェックは不要です(.Any()
チェックを入れたい場合がありますあなたのビジネスロジック上)。ただし、_q.Questions
_または_q.AnswerRows
_がnullの場合も、上記のコードで例外が発生することはありません。