c# - entre - Método de extensión LINQ SelectMany y Where ignorando nulos
diferencia entre select y selectmany (2)
Tengo el siguiente código de ejemplo, y me interesa saber cómo puedo hacer esto más limpio, posiblemente mediante un mejor uso de SelectMany()
. En este punto, la propiedad QuestionList
no será nula. Todo lo que quiero es una lista de answerRows
que no sean nulas, pero las Questions
veces también pueden ser nulas.
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;
ACTUALIZACIÓN: Cambié mi código ligeramente porque mi ejemplo no fue lo suficientemente claro con el uso de var
La pregunta era más para ayudarme a aprender más sobre el uso de LINQ.
ACTUALIZACIÓN 2:
Me interesó el comentario de Jon sobre Enumerable.SelectMany
y Null ... así que quise probar mi ejemplo con algunos datos falsos para ver más fácilmente dónde está el error. Consulte a continuación, específicamente cómo estoy utilizando SelectMany()
en el resultado. de un SelectMany()
, es más claro para mí ahora que el problema fue tener que asegurarse de no utilizar SelectMany()
en una referencia nula, lo cual es obvio cuando en realidad leí el nombre de NullReferenceException
:( y finalmente puse las cosas juntas.
Además, al hacer esto, me di cuenta de que el uso de try { } catch() { }
en este ejemplo es inútil y, como es habitual, Jon Skeet tiene la answer :) ejecución diferida ...
así que si desea ver la excepción para la fila 2, comente los bits relevantes de la fila 1: P, lo siento, no pude averiguar cómo detener este error sin volver a escribir el ejemplo de código.
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; }
}
}
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);
}
Esto le permite hacer lo siguiente:
var allAnswers = survey.QuestionList
.SelectNotNull(list => list.Questions)
.SelectNotNull(question => question.AnswerRows);
survey.QuestionList
.Where(l => l.Questions != null)
.SelectMany(l => l.Questions)
.Where(q => q != null && q.AnswerRows != null)
.SelectMany(q => q.AnswerRows);
Te recomiendo que te asegures de que tus colecciones nunca sean null
. null
puede ser un poco molesto si no lo manejas bien. Terminas con if (something != null) {}
todo tu código. Luego use:
survey.QuestionList
.SelectMany(l => l.Questions)
.SelectMany(q => q.AnswerRows);