2013-01-22 56 views
13

我有下面的示例代碼,我很想知道如何通過更好地使用SelectMany()來使這個更清潔。此時QuestionList屬性不會爲空。我想要的是一個不爲空的answerRows列表,但Questions有時也可以爲空。LINQ SelectMany和Where擴展方法忽略空值

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和空.. 所以我想嘗試用一些假的數據我的例子更容易地看到那裏的錯誤,請參閱下面的,具體地說,我如何使用SelectMany()SelectMany()的結果,它對我來說更清楚,現在問題是不得不確保在空引用時不使用SelectMany(),顯然當我實際讀取NullReferenceException名稱:(最後把東西放在一起

同時doin g這個,我意識到在這個例子中使用try { } catch() { }是沒用的,像往常一樣,Jon Skeet的answer :)推遲執行..

所以如果你想查看第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

爲什麼你認爲你的收藏品將永遠是空? –

+3

'questions'和'answerRows'永遠不能爲null。而在一個理智的設計中,'q.Questions'和'q.AnswerRows'可能永遠也不應該是'null'。 – Jon

+0

有時'if(!question.HasAnswer)return;' – spajce

回答

24
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); 
+0

謝謝,回答了我的問題..是的,每個人都閱讀此..避免NULL,當你可以。 – Pricey

+1

@Pricey。樂趣。這是「你不能總是相信選票」的好例子! :) –

+0

@Pricey。不要忘記標記爲正確:) –

7
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); 
+0

不是一個壞的方式。 – MuhammadHani

+0

+1謝謝你的擴展例子,這非常有幫助。 – Pricey

+0

「TResult:class」 - 是防止使用long和int序列的不必要的限制。最後「哪裏」也是不需要的,如果有必要的話,可以在「外面」添加它。 – greatvovan