C# LINQ SelectMany 和 Where 扩展方法忽略空值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14469159/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 11:54:36  来源:igfitidea点击:

LINQ SelectMany and Where extension method ignoring nulls

c#linq.net-4.0linq-to-objects

提问by Pricey

I have the below example code, and I am interested to know how I can make this any cleaner, possibly through better use of SelectMany(). At this point the QuestionListproperty will not be null. All I want is a list of answerRowsthat are not null, but Questionscan sometimes be null too.

我有下面的示例代码,我很想知道如何使它更干净,可能是通过更好地使用SelectMany(). 此时该QuestionList属性不会为空。我想要的只是一个answerRows不为空的列表,但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;

UPDATE:Changed my code slightly because my example wasn't clear enough with the use of var

更新:稍微更改了我的代码,因为我的示例在使用var

The question was more for helping me learn more about the use of LINQ.

问题更多是为了帮助我更多地了解 LINQ 的使用。

UPDATE 2:

更新 2:

I was interested by Jon's comment about Enumerable.SelectManyand Null.. so I wanted to try my example with some fake data to more easily see where the error is, please see the below, specifically how I am using SelectMany()on the result of a SelectMany(), its clearer to me now that the problem was having to make sure you don't use SelectMany()on a null reference, obvious when I actually read the NullReferenceExceptionname :( and finally put things together.

我对 Jon 的关于Enumerable.SelectMany和 Null的评论很感兴趣.. 所以我想用一些假数据来尝试我的例子,以便更容易地看到错误在哪里,请看下面,特别是我如何使用SelectMany()a 的结果SelectMany(),它更清楚我现在的问题是必须确保您不要SelectMany()在空引用上使用,这在我实际阅读NullReferenceException名称时很明显:( 最后把东西放在一起。

Also while doing this, I realised that the use of try { } catch() { }in this example is useless and as usual Jon Skeet has the answer:) deferred execution..

同样在这样做时,我意识到try { } catch() { }在这个例子中使用 是没用的,像往常一样,Jon Skeet 有答案:) 延迟执行..

so if you want to see the exception for row 2, comment out the relevant row 1 bits :P, sorry I couldn't figure out how to stop this error without re-writing the code example.

因此,如果您想查看第 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; }
    }
}

采纳答案by Paul Fleming

survey.QuestionList
    .Where(l => l.Questions != null)
    .SelectMany(l => l.Questions)
    .Where(q => q != null && q.AnswerRows != null)
    .SelectMany(q => q.AnswerRows);

I'd recommend you ensure your collections are never null. nullcan be a bit of a nuisance if you don't handle it well. You end up with if (something != null) {}all over your code. Then use:

我建议你确保你的收藏永远不会nullnull如果处理不好,可能会有点麻烦。你最终得到if (something != null) {}了你的代码。然后使用:

survey.QuestionList
    .SelectMany(l => l.Questions)
    .SelectMany(q => q.AnswerRows);

回答by Servy

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

This then allows you to do the following:

这允许您执行以下操作:

var allAnswers = survey.QuestionList
    .SelectNotNull(list => list.Questions)
    .SelectNotNull(question => question.AnswerRows);

回答by Neo

A solution that complies with DRY would be to use the null-coalescing operator ??in your SelectManylambda expression.

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

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

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

In both the OP's code and the above code, questionsand answerRowswill never be null, so the null checks are not required (you may wish to put .Any()checks depending on your business logic). But the above code will also never result in an exception if q.Questionsor q.AnswerRowsis null.

在 OP 的代码和上面的代码中,questions并且answerRows永远不会为空,因此不需要空检查(您可能希望.Any()根据您的业务逻辑进行检查)。但是如果q.Questionsq.AnswerRows为空,上面的代码也永远不会导致异常。