web-dev-qa-db-ja.com

nullを無視するLINQ SelectManyおよびWhere拡張メソッド

以下のコード例があります。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; }
    }
}
26
Pricey
_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);
_
41
Paul Fleming
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);
8
Servy

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のコードと上記のコードの両方で、questionsanswerRowsがnullになることはないため、nullチェックは不要です(.Any()チェックを入れたい場合がありますあなたのビジネスロジック上)。ただし、_q.Questions_または_q.AnswerRows_がnullの場合も、上記のコードで例外が発生することはありません。

0
Neo