LINQ 选择多个和 Where 扩展方法忽略空值



我有下面的示例代码,我很想知道如何通过更好地使用SelectMany()来使其更干净。此时,QuestionList属性不会为空。我想要的只是一个不null answerRows列表,但有时也可以null Questions

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;

我对乔恩关于Enumerable.SelectMany和空的评论很感兴趣。所以我想用一些假数据尝试我的例子,以便更容易地查看错误在哪里,请参阅以下内容,特别是我如何在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; }
    }
}
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);

符合 DRY 的解决方案是在 SelectMany lambda 表达式中使用空合并运算符??

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.Questionsq.AnswerRows 为 null,上面的代码也永远不会导致异常。

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

使用一些简短且可重用的东西:

public static IEnumerable<T> OrEmpty<T>(this IEnumerable<T> enumerable)
{
    return enumerable ?? Enumerable.Empty<T>();
}

然后在代码中,它看起来像:

survey.QuestionList.SelectMany(q => q.Questions.OrEmpty())

相关内容

  • 没有找到相关文章

最新更新