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
LINQ SelectMany and Where extension method ignoring nulls
提问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 QuestionList
property will not be null. All I want is a list of answerRows
that are not null, but Questions
can 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.SelectMany
and 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 NullReferenceException
name :( 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
. null
can 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:
我建议你确保你的收藏永远不会null
。null
如果处理不好,可能会有点麻烦。你最终得到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 SelectMany
lambda 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, questions
and answerRows
will 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.Questions
or q.AnswerRows
is null.
在 OP 的代码和上面的代码中,questions
并且answerRows
永远不会为空,因此不需要空检查(您可能希望.Any()
根据您的业务逻辑进行检查)。但是如果q.Questions
或q.AnswerRows
为空,上面的代码也永远不会导致异常。