Подтвердить что ты не робот

Метод LINQ SelectMany и Where extension, игнорирующий нули

У меня есть приведенный ниже пример кода, и мне интересно узнать, как я могу сделать это чище, возможно, благодаря более эффективному использованию SelectMany(). На этом этапе свойство QuestionList не будет равно 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(), тем более ясным для меня, что проблема должен был убедиться, что вы не используете SelectMany() по нулевой ссылке, очевидно, когда я действительно читаю имя NullReferenceException:( и, наконец, объединяю вещи.

И при этом я понял, что использование try { } catch() { } в этом примере бесполезно, и, как обычно, у Jon Skeet есть ответ:) отложенное исполнение..

поэтому, если вы хотите увидеть исключение для строки 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; }
    }
}
4b9b3361

Ответ 1

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

Ответ 2

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

Ответ 3

Решение, которое соответствует DRY, состояло бы в том, чтобы использовать оператор объединения нуля ?? в вашем лямбда-выражении SelectMany.

IEnumerable<IQuestion> questions = survey.QuestionList.SelectMany(q => q.Questions ?? Enumerable.Empty<IQuestion>());

if (questions is null) return null;

IEnumerable<IAnswerRow> answerRows = questions.SelectMany(q => q.AnswerRows ?? Enumerable.Empty<IAnswerRow>());

if (answerRows is null) return null;