Метод 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; }
}
}
Ответы
Ответ 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;